Exemple #1
0
class FindInFilesActions(QWidget):

    searchRequested = pyqtSignal('QString', bool, bool, bool)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Ignored)
        self._scope = QComboBox()
        self._scope.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed)
        self.ninjaide = IDE.get_service('ide')
        self.ninjaide.filesAndProjectsLoaded.connect(
            self._update_combo_projects)

        main_layout = QVBoxLayout(self)
        hbox = QHBoxLayout()
        hbox.addWidget(QLabel(translations.TR_SEARCH_SCOPE))
        hbox.addWidget(self._scope)
        main_layout.addLayout(hbox)
        widgets_layout = QGridLayout()
        widgets_layout.setContentsMargins(0, 0, 0, 0)
        self._line_search = QLineEdit()
        self._line_search.setPlaceholderText(translations.TR_SEARCH_FOR)
        main_layout.addWidget(self._line_search)
        # TODO: replace
        self._check_cs = QCheckBox(translations.TR_SEARCH_CASE_SENSITIVE)
        self._check_cs.setChecked(True)
        widgets_layout.addWidget(self._check_cs, 2, 0)
        self._check_wo = QCheckBox(translations.TR_SEARCH_WHOLE_WORDS)
        widgets_layout.addWidget(self._check_wo, 2, 1)
        self._check_re = QCheckBox(translations.TR_SEARCH_REGEX)
        widgets_layout.addWidget(self._check_re, 3, 0)
        self._check_recursive = QCheckBox('Recursive')
        widgets_layout.addWidget(self._check_recursive, 3, 1)
        main_layout.addLayout(widgets_layout)
        main_layout.addStretch(1)

        # Connections
        self._line_search.returnPressed.connect(self.search_requested)

    def _update_combo_projects(self):
        projects = self.ninjaide.get_projects()
        for nproject in projects.values():
            self._scope.addItem(nproject.name, nproject.path)

    @property
    def current_project_path(self):
        """Returns NProject.path of current project"""
        return self._scope.itemData(self._scope.currentIndex())

    def search_requested(self):
        text = self._line_search.text()
        if not text.strip():
            return
        has_search = self._line_search.text()
        cs = self._check_cs.isChecked()
        regex = self._check_re.isChecked()
        wo = self._check_wo.isChecked()
        self.searchRequested.emit(has_search, cs, regex, wo)
Exemple #2
0
class PreviewForm(QDialog):
    def __init__(self, parent):
        super(PreviewForm, self).__init__(parent)

        self.encodingComboBox = QComboBox()
        encodingLabel = QLabel("&Encoding:")
        encodingLabel.setBuddy(self.encodingComboBox)

        self.textEdit = QTextEdit()
        self.textEdit.setLineWrapMode(QTextEdit.NoWrap)
        self.textEdit.setReadOnly(True)

        buttonBox = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)

        self.encodingComboBox.activated.connect(self.updateTextEdit)
        buttonBox.accepted.connect(self.accept)
        buttonBox.rejected.connect(self.reject)

        mainLayout = QGridLayout()
        mainLayout.addWidget(encodingLabel, 0, 0)
        mainLayout.addWidget(self.encodingComboBox, 0, 1)
        mainLayout.addWidget(self.textEdit, 1, 0, 1, 2)
        mainLayout.addWidget(buttonBox, 2, 0, 1, 2)
        self.setLayout(mainLayout)

        self.setWindowTitle("Choose Encoding")
        self.resize(400, 300)

    def setCodecList(self, codecs):
        self.encodingComboBox.clear()
        for codec in codecs:
            self.encodingComboBox.addItem(codec_name(codec), codec.mibEnum())

    def setEncodedData(self, data):
        self.encodedData = data
        self.updateTextEdit()

    def decodedString(self):
        return self.decodedStr

    def updateTextEdit(self):
        mib = self.encodingComboBox.itemData(self.encodingComboBox.currentIndex())
        codec = QTextCodec.codecForMib(mib)

        data = QTextStream(self.encodedData)
        data.setAutoDetectUnicode(False)
        data.setCodec(codec)

        self.decodedStr = data.readAll()
        self.textEdit.setPlainText(self.decodedStr)
Exemple #3
0
class Settings_Misc(QWidget):
    def __init__(self, parent: QWidget):
        super(Settings_Misc, self).__init__(parent)
        self._layout = QVBoxLayout()
        self.setLayout(self._layout)
        # tray icon settings
        self._l_ti = QHBoxLayout()
        self._lbl_ti = QLabel(self.tr("System tray icon:"), self)
        self._cb_ti = QComboBox(self)
        self._cb_ti.addItem(self.tr("Do not use"), QVariant("none"))
        self._cb_ti.addItem(self.tr("Show"), QVariant("show"))
        self._cb_ti.addItem(self.tr("Show, minimize to tray"), QVariant("show_min"))
        # finalize layout
        self._l_ti.addWidget(self._lbl_ti)
        self._l_ti.addWidget(self._cb_ti)
        self._layout.addLayout(self._l_ti)
        self._layout.addStretch()
        #
        self.hide()

    def save_to_config(self, cfg: configparser.ConfigParser):
        # ensure there is a 'net' section
        if "tray" not in cfg:
            cfg.add_section("tray")
        # tray icon settings
        idx = self._cb_ti.currentIndex()
        if idx >= 0:
            usage = str(self._cb_ti.itemData(idx, Qt.UserRole))
            cfg["tray"]["icon_usage"] = usage
        logger.debug("Saved misc config")

    def load_from_config(self, cfg: configparser.ConfigParser):
        # defaults
        usage = "none"
        if "tray" in cfg:
            usage = cfg["tray"]["icon_usage"]
        if usage == "none":
            self._cb_ti.setCurrentIndex(0)
        elif usage == "show":
            self._cb_ti.setCurrentIndex(1)
        elif usage == "show_min":
            self._cb_ti.setCurrentIndex(2)
        else:
            raise ValueError("Ivalid tray icon usage setting: " + usage)
class MainWindow(QWidget):
    # constructor
    def __init__(self):
        super().__init__()
        # changing the background color to yellow
        # self.setStyleSheet("background-color: #ece2e1;color:#000;")
        # core
        self.core = Core()
        # translate function
        # list of italy regions
        self.italy_regions = self.core.get_region()
        # list of european region
        self.european_countries = self.core.get_european_countries()
        # initialSetup
        self.initialSetup()

    # initialSetup
    def initialSetup(self):
        # set title
        self.setWindowTitle("Italy Covid Report 2021")
        # set window screen
        self.setFixedSize(1300, 800)
        # show content
        self.contentWidget()

        # show the window
        self.show()

    def contentWidget(self):
        # select region v box 1
        select_region_h_box1 = QHBoxLayout()
        # selection one notice
        select_region_h_box1.addSpacing(10)
        # selection label 1
        self.select_region_label = QLabel("Select Region", self)
        select_region_h_box1.addWidget(self.select_region_label)
        # drop down button
        self.select_region_combo = QComboBox(self)
        self.select_region_combo.addItems(self.italy_regions)
        select_region_h_box1.addWidget(self.select_region_combo)
        # push button
        self.select_region_botton = QPushButton("Check Region", self)
        self.select_region_botton.pressed.connect(self.italyRegionClicked)
        select_region_h_box1.addWidget(self.select_region_botton)

        # select region v box 2
        select_region_h_box2 = QHBoxLayout()
        select_region_h_box2.addSpacing(10)
        # selection label 1
        self.select_region_label = QLabel("Italy against", self)
        select_region_h_box2.addWidget(self.select_region_label)

        # drop down button (combo )
        self.select_euro_combo = QComboBox(self)
        self.select_euro_combo.addItems(self.european_countries)
        select_region_h_box2.addWidget(self.select_euro_combo)
        # push button
        self.compare_button = QPushButton("Compare", self)
        self.compare_button.pressed.connect(self.europeanCountriesClicked)
        select_region_h_box2.addWidget(self.compare_button)

        # selection main V box
        select_region_main_v_box = QVBoxLayout()

        # An overall language pull down menu is being requested at the very top of the GUI
        # with the option of English and Italian labelling
        language_h_box = QHBoxLayout()
        self.language_lbl = QLabel("Select Language", self)
        self.language_combobox = QComboBox()
        # language option
        options = ([
            ('English', 'en'),
            ('Italian', 'it'),
            ('Spanish', 'es'),
            ('Chinese', 'zh-CN'),
        ])
        # add language and change language
        for i, (text, lang) in enumerate(options):
            self.language_combobox.addItem(text)
            self.language_combobox.setItemData(i, lang)

        language_h_box.addWidget(self.language_lbl)
        # on index changed
        self.language_combobox.currentIndexChanged.connect(
            self.languageChanged)

        language_h_box.addWidget(self.language_combobox)
        language_h_box.addStretch()
        # add  language_h_box layout
        select_region_main_v_box.addLayout(language_h_box)

        # Italy Region Covid Report
        self.italy_lbl = QLabel("Italy Region Covid Report", self)
        self.italy_lbl.setStyleSheet("border: 0.5px solid gray")
        select_region_main_v_box.addWidget(self.italy_lbl)
        select_region_main_v_box.addLayout(select_region_h_box1)
        select_region_main_v_box.setSpacing(15)
        self.euro_text = QLabel(
            "Italy Covid report against European countries", self)
        self.euro_text.setStyleSheet("border: 0.5px solid gray")
        select_region_main_v_box.addWidget(self.euro_text)
        select_region_main_v_box.addLayout(select_region_h_box2)
        select_region_main_v_box.addStretch()

        # for region map and demographic
        region_map_box = QVBoxLayout()

        self.coordinate_title = "This is a title"
        self.coordinate = coordinate['Campania']

        m = folium.Map(tiles="Stamen Terrain",
                       zoom_start=6,
                       location=self.coordinate)

        # create HTML for pop up
        def foliumHtml(lo):
            # get stats
            if lo != "Italy":
                stats = self.core.getRegionStats(str(lo))
                return f"""
                 <h1 style='color:#7b113a;'> {lo} </h1>
                 <hr/>
                 <p style='color:#7b113a;font-size:20px;'>Region Population: {stats['region_population']}</p>
                 <p style='color:#7b113a;font-size:20px;'>Total Covid Case: {stats['case_number']}</p>
                 <p style='color:#7b113a;font-size:20px;'>Daily Cases: {stats['expectedChanges']}</p>
                 <p style='color:#7b113a;font-size:20px;'>Percentage: {stats['percentage']}%</p>
                 """
            else:
                return f"""
                 <h1> {lo}</h1>
                 <p>European country with a long Mediterranean coastline, has left a powerful mark on Western culture and cuisine.</p>
                 """

        # add marker one by one on the map
        for lo in coordinate:
            # add pop ups
            html = foliumHtml(lo)
            iframe = folium.IFrame(html=html, width=300, height=250)
            popUp = folium.Popup(iframe, max_width=2650)
            # Marker starts here
            folium.Marker(location=coordinate[lo],
                          popup=popUp,
                          icon=folium.DivIcon(html=f"""
                     <div><svg>
                         <circle cx="50" cy="50" r="40" fill="#7b113a" opacity=".4"/>
                         <rect x="35", y="35" width="30" height="30", fill="#fff600", opacity=".3" 
                     </svg></div>""")).add_to(m)

        # save map data to data object
        data = io.BytesIO()
        m.save(data, close_file=False)

        webView = QWebEngineView()
        webView.setHtml(data.getvalue().decode())
        region_map_box.addWidget(webView)

        # main box top - bottom
        h_box = QHBoxLayout()
        h_box.addLayout(select_region_main_v_box)
        h_box.addLayout(region_map_box)
        self.setLayout(h_box)

    # languageChanged clicked

    @qt.pyqtSlot(int)
    def languageChanged(self, index):
        data = self.language_combobox.itemData(index)

        translator = Translator()
        # print(translator.translate('Hello.', dest=data).text)
        # select language
        self.language_lbl.setText(
            translator.translate('Select Language.', dest=data).text)
        # selection headings
        self.italy_lbl.setText(
            translator.translate('Italy Region Covid Report', dest=data).text)
        self.euro_text.setText(
            translator.translate(
                'Italy Covid report against european countries.',
                dest=data).text)
        #
        #
        # # italy selection region text
        self.select_region_label.setText(
            translator.translate('Select Region.', dest=data).text)
        self.select_region_botton.setText(
            translator.translate('Check Region', dest=data).text)
        # compare section
        self.select_region_label.setText(
            translator.translate('Italy Against.', dest=data).text)
        self.compare_button.setText(
            translator.translate('Compare.', dest=data).text)

    # this method when clicked get and display region statistics
    # italyRegionClicked
    def italyRegionClicked(self):
        # check if value returned is not None
        if self.select_region_combo.currentText() is not None:
            stats = self.core.getRegionStats(
                self.select_region_combo.currentText())
            self.italyRegionStatistics(stats)
        else:
            self.errorMessage("Error getting region")

    # on clicked this method display the statistic between italy and euro countries
    # europeanCountriesClicked
    def europeanCountriesClicked(self):
        if self.select_euro_combo.currentText() is not None:
            # get italy stats
            italy_stats = self.core.getItalyStats("Italy")
            euro_stats = self.core.getEuropeanCountryStats(
                self.select_euro_combo.currentText())
            # display stats dialog
            self.compareStatisticsDialog(italy_stats, euro_stats)

        else:
            self.errorMessage("Error comparing countries")

    # for handling error message
    def errorMessage(self, message):
        QMessageBox().warning(self, "Unexpected Error", message,
                              QMessageBox.Ok, QMessageBox.Ok)

    # display italy region statistics
    def italyRegionStatistics(self, stats):
        # create dialog
        regionDialog = QDialog(self)
        regionDialog.setFixedSize(390, 230)
        # set dialog title
        regionDialog.setWindowTitle(f"{stats['region']} Report".capitalize())
        # show region

        # dialog title
        d_title = QLabel(f"{stats['region']}", self)
        d_title.setFont(QFont('Times', 25, QFont.Light))
        d_title.setAlignment(qt.Qt.AlignCenter)
        # h rows
        r1_box = QHBoxLayout()
        r1_box.addStretch()
        region_label = QLabel('Region :', self)
        region_label.setFont(QFont('Times', 25, QFont.Light))
        region_text = QLabel(f"{stats['region']}", self)
        region_text.setFont(QFont('Times', 25, QFont.Light))
        r1_box.addWidget(region_label)
        r1_box.addWidget(region_text)
        r1_box.addStretch()

        # 2 h rows
        r2_box = QHBoxLayout()
        r2_box.addStretch()
        population_label = QLabel('Population :', self)
        population_label.setFont(QFont('Times', 25, QFont.Light))
        population_text = QLabel(f"{stats['population']}", self)
        population_text.setFont(QFont('Times', 25, QFont.Light))
        r2_box.addWidget(population_label)
        r2_box.addWidget(population_text)
        r2_box.addStretch()

        # 3 h rows
        r3_box = QHBoxLayout()
        r3_box.addStretch()
        region_population_label = QLabel('Region Population :', self)
        region_population_label.setFont(QFont('Times', 25, QFont.Light))
        region_population_text = QLabel(f"{stats['region_population']}", self)
        region_population_text.setFont(QFont('Times', 25, QFont.Light))
        r3_box.addWidget(region_population_label)
        r3_box.addWidget(region_population_text)
        r3_box.addStretch()

        # 4 h rows
        r4_box = QHBoxLayout()
        r4_box.addStretch()
        case_number_label = QLabel('Total Covid Case :', self)
        case_number_label.setFont(QFont('Times', 25, QFont.Light))
        case_number_text = QLabel(f"{stats['case_number']}", self)
        case_number_text.setFont(QFont('Times', 25, QFont.Light))
        r4_box.addWidget(case_number_label)
        r4_box.addWidget(case_number_text)
        r4_box.addStretch()

        # 5 h rows
        r5_box = QHBoxLayout()
        r5_box.addStretch()
        expected_changes_label = QLabel('Daily Cases  :', self)
        expected_changes_label.setFont(QFont('Times', 25, QFont.Light))
        expected_changes_text = QLabel(f"{stats['expectedChanges']}", self)
        expected_changes_text.setFont(QFont('Times', 25, QFont.Light))
        r5_box.addWidget(expected_changes_label)
        r5_box.addWidget(expected_changes_text)
        r5_box.addStretch()

        # 6 h rows
        r6_box = QHBoxLayout()
        r6_box.addStretch()
        cal_percentage_label = QLabel('Percentage :', self)
        cal_percentage_label.setFont(QFont('Times', 25, QFont.Light))
        cal_percentage_text = QLabel(f"{stats['percentage']}%", self)
        cal_percentage_text.setFont(QFont('Times', 25, QFont.Light))
        r6_box.addWidget(cal_percentage_label)
        r6_box.addWidget(cal_percentage_text)
        r6_box.addStretch()

        # main v box
        main_v_box = QVBoxLayout()
        main_v_box.setSpacing(10)
        # add row 1
        main_v_box.addLayout(r1_box)
        # add row 2
        main_v_box.addLayout(r2_box)
        # add row 3
        main_v_box.addLayout(r3_box)
        # add row 4
        main_v_box.addLayout(r4_box)
        # add row 5
        main_v_box.addLayout(r5_box)
        # add row 6
        main_v_box.addLayout(r6_box)
        main_v_box.addStretch()

        regionDialog.setLayout(main_v_box)
        regionDialog.exec_()

    # comparing italy and other european country
    def compareStatisticsDialog(self, italy_stats, euro_stats):
        # create dialog
        regionDialog = QDialog(self)
        regionDialog.setFixedSize(700, 300)
        # set dialog title
        regionDialog.setWindowTitle(f"{euro_stats['country']}")
        # show comparison
        # left and right v box
        left_v_box = QVBoxLayout()
        right_v_box = QVBoxLayout()

        # horizontal box

        # Left H box 1
        left_h_box_1 = QHBoxLayout()
        left_h_box_1.addStretch()
        country_lbl = QLabel("Country: ", self)
        country_lbl.setFont(QFont('Times', 20, QFont.Light))
        left_h_box_1.addWidget(country_lbl)
        country_text = QLabel(italy_stats['country'], self)
        country_text.setFont(QFont('Times', 20, QFont.Light))
        left_h_box_1.addWidget(country_text)
        left_h_box_1.addStretch()
        # add layout
        left_v_box.addLayout(left_h_box_1)

        # Right H box 1
        right_h_box_1 = QHBoxLayout()
        right_h_box_1.addStretch()
        euro_country_lbl = QLabel("Country: ", self)
        euro_country_lbl.setFont(QFont('Times', 20, QFont.Light))
        right_h_box_1.addWidget(euro_country_lbl)
        euro_country_text = QLabel(euro_stats['country'], self)
        euro_country_text.setFont(QFont('Times', 20, QFont.Light))
        right_h_box_1.addWidget(euro_country_text)
        right_h_box_1.addStretch()
        # add layout
        right_v_box.addLayout(right_h_box_1)

        # Left H box 2
        left_h_box_2 = QHBoxLayout()
        left_h_box_2.addStretch()
        population_lbl = QLabel("Total Population: ", self)
        population_lbl.setFont(QFont('Times', 20, QFont.Light))
        left_h_box_2.addWidget(population_lbl)
        population_text = QLabel(str(italy_stats['population']), self)
        population_text.setFont(QFont('Times', 20, QFont.Light))
        left_h_box_2.addWidget(population_text)
        left_h_box_2.addStretch()
        # add layout
        left_v_box.addLayout(left_h_box_2)

        # Right H box 2
        right_h_box_2 = QHBoxLayout()
        right_h_box_2.addStretch()
        euro_population_lbl = QLabel("Total Population: ", self)
        euro_population_lbl.setFont(QFont('Times', 20, QFont.Light))
        right_h_box_2.addWidget(euro_population_lbl)
        euro_population_text = QLabel(str(euro_stats['population']), self)
        euro_population_text.setFont(QFont('Times', 20, QFont.Light))
        right_h_box_2.addWidget(euro_population_text)
        right_h_box_2.addStretch()
        # add layout
        right_v_box.addLayout(right_h_box_2)

        # Left H box 3
        left_h_box_3 = QHBoxLayout()
        left_h_box_3.addStretch()
        case_lbl = QLabel("Total Case: ", self)
        case_lbl.setFont(QFont('Times', 20, QFont.Light))
        left_h_box_3.addWidget(case_lbl)
        case_text = QLabel(str(italy_stats['case']), self)
        case_text.setFont(QFont('Times', 20, QFont.Light))
        left_h_box_3.addWidget(case_text)
        left_h_box_3.addStretch()
        # add layout
        left_v_box.addLayout(left_h_box_3)

        # Right H box 3
        right_h_box_3 = QHBoxLayout()
        right_h_box_3.addStretch()
        euro_case_lbl = QLabel("Total Case: ", self)
        euro_case_lbl.setFont(QFont('Times', 20, QFont.Light))
        right_h_box_3.addWidget(euro_case_lbl)
        euro_case_text = QLabel(str(euro_stats['case']), self)
        euro_case_text.setFont(QFont('Times', 20, QFont.Light))
        right_h_box_3.addWidget(euro_case_text)
        right_h_box_3.addStretch()
        # add layout
        right_v_box.addLayout(right_h_box_3)

        # Left H box 4
        left_h_box_4 = QHBoxLayout()
        left_h_box_4.addStretch()
        rate_lbl = QLabel("Infection Rate: ", self)
        rate_lbl.setFont(QFont('Times', 20, QFont.Light))
        left_h_box_4.addWidget(rate_lbl)
        rate_text = QLabel(str(italy_stats['rate']), self)
        rate_text.setFont(QFont('Times', 20, QFont.Light))
        left_h_box_4.addWidget(rate_text)
        left_h_box_4.addStretch()
        # add layout
        left_v_box.addLayout(left_h_box_4)

        # Right H box 4
        right_h_box_4 = QHBoxLayout()
        right_h_box_4.addStretch()
        euro_rate_lbl = QLabel("Infection Rate: ", self)
        euro_rate_lbl.setFont(QFont('Times', 20, QFont.Light))
        right_h_box_4.addWidget(euro_rate_lbl)
        euro_rate_text = QLabel(str(euro_stats['rate']), self)
        euro_rate_text.setFont(QFont('Times', 20, QFont.Light))
        right_h_box_4.addWidget(euro_rate_text)
        right_h_box_4.addStretch()
        # add layout
        right_v_box.addLayout(right_h_box_4)

        # inner h box
        second_main_h_box = QHBoxLayout()
        # add right and left v box
        second_main_h_box.addLayout(left_v_box)
        second_main_h_box.addLayout(right_v_box)

        # main v box
        main_v_box = QVBoxLayout()
        # set main title
        main_v_title = QLabel(
            f"Comparison reports between Italy and {euro_stats['country']}",
            self)
        main_v_title.setFont(QFont('Times', 20, QFont.Bold))
        main_v_title.setAlignment(qt.Qt.AlignCenter)

        main_v_box.addWidget(main_v_title)
        main_v_box.addSpacing(30)

        # add second_main_h_box
        main_v_box.addLayout(second_main_h_box)
        # push everything to the top
        main_v_box.addStretch()
        # set main layout
        regionDialog.setLayout(main_v_box)

        regionDialog.exec_()
Exemple #5
0
class AmbientLightV3(COMCUPluginBase):
    def __init__(self, *args):
        super().__init__(BrickletAmbientLightV3, *args)

        self.al = self.device

        self.cbe_illuminance = CallbackEmulator(self,
                                                self.al.get_illuminance,
                                                None,
                                                self.cb_illuminance,
                                                self.increase_error_count)

        self.alf = ColorFrame(25, 25, QColor(128, 128, 128))
        self.out_of_range_label = QLabel('Illuminance is out-of-range')
        self.invalid_label = QLabel('Illuminance is invalid')

        self.out_of_range_label.hide()
        self.out_of_range_label.setStyleSheet('QLabel { color: red }')
        self.invalid_label.hide()
        self.invalid_label.setStyleSheet('QLabel { color: magenta }')

        self.current_illuminance = CurveValueWrapper() # float, lx

        plots = [('Illuminance', Qt.red, self.current_illuminance, '{:.2f} lx (Lux)'.format)]
        self.plot_widget = PlotWidget('Illuminance [lx]', plots,
                                      extra_key_widgets=[self.out_of_range_label, self.invalid_label, self.alf],
                                      y_resolution=0.001)

        self.range_label = QLabel('Illuminance Range:')
        self.range_combo = QComboBox()

        self.range_combo.addItem("Unlimited", BrickletAmbientLightV3.ILLUMINANCE_RANGE_UNLIMITED)
        self.range_combo.addItem("0 - 64000 lx", BrickletAmbientLightV3.ILLUMINANCE_RANGE_64000LUX)
        self.range_combo.addItem("0 - 32000 lx", BrickletAmbientLightV3.ILLUMINANCE_RANGE_32000LUX)
        self.range_combo.addItem("0 - 16000 lx", BrickletAmbientLightV3.ILLUMINANCE_RANGE_16000LUX)
        self.range_combo.addItem("0 - 8000 lx", BrickletAmbientLightV3.ILLUMINANCE_RANGE_8000LUX)
        self.range_combo.addItem("0 - 1300 lx", BrickletAmbientLightV3.ILLUMINANCE_RANGE_1300LUX)
        self.range_combo.addItem("0 - 600 lx", BrickletAmbientLightV3.ILLUMINANCE_RANGE_600LUX)
        self.range_combo.currentIndexChanged.connect(self.new_config)

        self.time_label = QLabel('Integration Time:')
        self.time_combo = QComboBox()
        self.time_combo.addItem("50 ms", BrickletAmbientLightV3.INTEGRATION_TIME_50MS)
        self.time_combo.addItem("100 ms", BrickletAmbientLightV3.INTEGRATION_TIME_100MS)
        self.time_combo.addItem("150 ms", BrickletAmbientLightV3.INTEGRATION_TIME_150MS)
        self.time_combo.addItem("200 ms", BrickletAmbientLightV3.INTEGRATION_TIME_200MS)
        self.time_combo.addItem("250 ms", BrickletAmbientLightV3.INTEGRATION_TIME_250MS)
        self.time_combo.addItem("300 ms", BrickletAmbientLightV3.INTEGRATION_TIME_300MS)
        self.time_combo.addItem("350 ms", BrickletAmbientLightV3.INTEGRATION_TIME_350MS)
        self.time_combo.addItem("400 ms", BrickletAmbientLightV3.INTEGRATION_TIME_400MS)
        self.time_combo.currentIndexChanged.connect(self.new_config)

        hlayout = QHBoxLayout()
        hlayout.addWidget(self.range_label)
        hlayout.addWidget(self.range_combo)
        hlayout.addStretch()
        hlayout.addWidget(self.time_label)
        hlayout.addWidget(self.time_combo)

        line = QFrame()
        line.setObjectName("line")
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)

        layout = QVBoxLayout(self)
        layout.addWidget(self.plot_widget)
        layout.addWidget(line)
        layout.addLayout(hlayout)

    def start(self):
        async_call(self.al.get_configuration, None, self.get_configucation_async, self.increase_error_count)

        self.cbe_illuminance.set_period(100)

        self.plot_widget.stop = False

    def stop(self):
        self.cbe_illuminance.set_period(0)

        self.plot_widget.stop = True

    def destroy(self):
        pass

    @staticmethod
    def has_device_identifier(device_identifier):
        return device_identifier == BrickletAmbientLightV3.DEVICE_IDENTIFIER

    def get_configucation_async(self, conf):
        self.range_combo.setCurrentIndex(self.range_combo.findData(conf.illuminance_range))
        self.time_combo.setCurrentIndex(self.time_combo.findData(conf.integration_time))

    def new_config(self, _value):
        try:
            self.al.set_configuration(self.range_combo.itemData(self.range_combo.currentIndex()),
                                      self.time_combo.itemData(self.time_combo.currentIndex()))
        except ip_connection.Error:
            pass

    def cb_illuminance(self, illuminance):
        self.current_illuminance.value = illuminance / 100.0

        max_illuminance = 12000000 # Approximation for unlimited range
        current_range = self.range_combo.itemData(self.range_combo.currentIndex())

        if current_range == BrickletAmbientLightV3.ILLUMINANCE_RANGE_64000LUX:
            max_illuminance = 6400001
        elif current_range == BrickletAmbientLightV3.ILLUMINANCE_RANGE_32000LUX:
            max_illuminance = 3200001
        elif current_range == BrickletAmbientLightV3.ILLUMINANCE_RANGE_16000LUX:
            max_illuminance = 1600001
        elif current_range == BrickletAmbientLightV3.ILLUMINANCE_RANGE_8000LUX:
            max_illuminance = 800001
        elif current_range == BrickletAmbientLightV3.ILLUMINANCE_RANGE_1300LUX:
            max_illuminance = 130001
        elif current_range == BrickletAmbientLightV3.ILLUMINANCE_RANGE_600LUX:
            max_illuminance = 60001

        if illuminance == 0:
            self.plot_widget.get_key_item(0).setStyleSheet('QLabel { color: magenta }')
            self.out_of_range_label.hide()
            self.invalid_label.show()
        elif illuminance >= max_illuminance:
            self.plot_widget.get_key_item(0).setStyleSheet('QLabel { color: red }')
            self.out_of_range_label.show()
            self.invalid_label.hide()
        else:
            self.plot_widget.get_key_item(0).setStyleSheet('')
            self.out_of_range_label.hide()
            self.invalid_label.hide()

        value = min(max(illuminance * 255 // max_illuminance, 0), 255)
        self.alf.set_color(QColor(value, value, value))
class SchemeSelector(QWidget):

    currentChanged = pyqtSignal()
    changed = pyqtSignal()

    def __init__(self, parent=None):
        super(SchemeSelector, self).__init__(parent)
        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(layout)

        self.label = QLabel()
        self.scheme = QComboBox()
        self.menuButton = QPushButton(flat=True)
        menu = QMenu(self.menuButton)
        self.menuButton.setMenu(menu)
        layout.addWidget(self.label)
        layout.addWidget(self.scheme)
        layout.addWidget(self.menuButton)
        layout.addStretch(1)
        # action generator
        def act(slot, icon=None):
            a = QAction(self, triggered=slot)
            self.addAction(a)
            icon and a.setIcon(icons.get(icon))
            return a

        # add action
        a = self.addAction_ = act(self.slotAdd, 'list-add')
        menu.addAction(a)

        # remove action
        a = self.removeAction = act(self.slotRemove, 'list-remove')
        menu.addAction(a)


        # rename action
        a = self.renameAction = act(self.slotRename, 'document-edit')
        menu.addAction(a)

        menu.addSeparator()

        # import action
        a = self.importAction = act(self.slotImport, 'document-open')
        menu.addAction(a)

        # export action
        a = self.exportAction = act(self.slotExport, 'document-save-as')
        menu.addAction(a)


        self.scheme.currentIndexChanged.connect(self.slotSchemeChanged)
        app.translateUI(self)

    def translateUI(self):
        self.label.setText(_("Scheme:"))
        self.menuButton.setText(_("&Menu"))
        self.addAction_.setText(_("&Add..."))
        self.removeAction.setText(_("&Remove"))
        self.renameAction.setText(_("Re&name..."))
        self.importAction.setText(_("&Import..."))
        self.exportAction.setText(_("&Export..."))

    def slotSchemeChanged(self, index):
        """Called when the Scheme combobox is changed by the user."""
        self.disableDefault(self.scheme.itemData(index) == 'default')
        self.currentChanged.emit()
        self.changed.emit()

    def disableDefault(self, val):
        self.removeAction.setDisabled(val)
        self.renameAction.setDisabled(val)

    def schemes(self):
        """Returns the list with internal names of currently available schemes."""
        return [self.scheme.itemData(i) for i in range(self.scheme.count())]

    def currentScheme(self):
        """Returns the internal name of the currently selected scheme"""
        return self.scheme.itemData(self.scheme.currentIndex())

    def insertSchemeItem(self, name, scheme):
        for i in range(1, self.scheme.count()):
            n = self.scheme.itemText(i)
            if n.lower() > name.lower():
                self.scheme.insertItem(i, name, scheme)
                break
        else:
            self.scheme.addItem(name, scheme)

    def addScheme(self, name):
        num, key = 1, 'user1'
        while key in self.schemes() or key in self._schemesToRemove:
            num += 1
            key = 'user{0}'.format(num)
        self.insertSchemeItem(name, key)
        self.scheme.setCurrentIndex(self.scheme.findData(key))
        return key

    def slotAdd(self):
        name, ok = QInputDialog.getText(self,
            app.caption(_("Add Scheme")),
            _("Please enter a name for the new scheme:"))
        if ok:
            self.addScheme(name)


    def slotRemove(self):
        index = self.scheme.currentIndex()
        scheme = self.scheme.itemData(index)
        if scheme == 'default':
            return # default can not be removed

        self._schemesToRemove.add(scheme)
        self.scheme.removeItem(index)

    def slotRename(self):
        index = self.scheme.currentIndex()
        name = self.scheme.itemText(index)
        scheme = self.scheme.itemData(index)
        newName, ok = QInputDialog.getText(self, _("Rename"), _("New name:"), text=name)
        if ok:
            self.scheme.blockSignals(True)
            self.scheme.removeItem(index)
            self.insertSchemeItem(newName, scheme)
            self.scheme.setCurrentIndex(self.scheme.findData(scheme))
            self.scheme.blockSignals(False)
            self.changed.emit()

    def slotImport(self):
        filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"), _("All Files"))
        caption = app.caption(_("dialog title", "Import color theme"))
        filename = QFileDialog.getOpenFileName(self, caption, QDir.homePath(), filetypes)[0]
        if filename:
            self.parent().import_(filename)

    def slotExport(self):
        name = self.scheme.currentText()
        filetypes = "{0} (*.xml);;{1} (*)".format(_("XML Files"), _("All Files"))
        caption = app.caption(_("dialog title",
            "Export {name}").format(name=name))
        path = os.path.join(QDir.homePath(), name+'.xml')
        filename = QFileDialog.getSaveFileName(self, caption, path, filetypes)[0]
        if filename:
            if os.path.splitext(filename)[1] != '.xml':
                filename += '.xml'
            self.parent().export(name, filename)


    def loadSettings(self, currentKey, namesGroup):
        # don't mark schemes for removal anymore
        self._schemesToRemove = set()

        s = QSettings()
        cur = s.value(currentKey, "default", str)

        # load the names for the shortcut schemes
        s.beginGroup(namesGroup)
        block = self.scheme.blockSignals(True)
        self.scheme.clear()
        self.scheme.addItem(_("Default"), "default")
        lst = [(s.value(key, key, str), key) for key in s.childKeys()]
        for name, key in sorted(lst, key=lambda f: f[0].lower()):
            self.scheme.addItem(name, key)

        # find out index
        index = self.scheme.findData(cur)
        self.disableDefault(cur == 'default')
        self.scheme.setCurrentIndex(index)
        self.scheme.blockSignals(block)
        self.currentChanged.emit()

    def saveSettings(self, currentKey, namesGroup, removePrefix=None):
        # first save new scheme names
        s = QSettings()
        s.beginGroup(namesGroup)
        for i in range(self.scheme.count()):
            if self.scheme.itemData(i) != 'default':
                s.setValue(self.scheme.itemData(i), self.scheme.itemText(i))

        for scheme in self._schemesToRemove:
            s.remove(scheme)
        s.endGroup()
        if removePrefix:
            for scheme in self._schemesToRemove:
                s.remove("{0}/{1}".format(removePrefix, scheme))
        # then save current
        scheme = self.currentScheme()
        s.setValue(currentKey, scheme)
        # clean up
        self._schemesToRemove = set()
Exemple #7
0
class addTileDialog(QDialog):
    output = None
    ready = False

    def __init__(self):
        super().__init__()
        self.tilename = QComboBox()
        for tile in tilelist.tilelist:
            self.tilename.addItem(tile.__name__, tile)
        self.tilename.currentIndexChanged.connect(self.setTable)
        okbtn = QPushButton('Ok')
        okbtn.clicked.connect(self.onOk)
        clbtn = QPushButton('Cancel')
        clbtn.clicked.connect(self.onCancel)
        self.table = QTableWidget(2, 2)
        self.setTable(self.tilename.currentIndex())
        hbox = QHBoxLayout()
        hbox.addWidget(clbtn)
        hbox.addWidget(okbtn)
        vbox = QVBoxLayout()
        vbox.addWidget(self.tilename)
        vbox.addWidget(self.table)
        vbox.addLayout(hbox)
        self.setLayout(vbox)
        self.resize(400, 300)
        self.table.setColumnWidth(1, 250)
        self.setModal(True)
        self.show()

    def setTable(self, index):
        try:
            raw = 2 + len(self.tilename.itemData(index).extraText)
        except TypeError:
            raw = 2
        self.table.setRowCount(raw)
        self.table.setItem(0, 0, QTableWidgetItem('X position'))
        self.table.setCellWidget(0, 1, QSpinBox())
        self.table.setItem(1, 0, QTableWidgetItem('Y position'))
        self.table.setCellWidget(1, 1, QSpinBox())
        if self.tilename.itemData(index).extraText != None:
            i = 0
            for line in self.tilename.itemData(index).extraText:
                self.table.setItem(2 + i, 0, QTableWidgetItem(line))
                self.table.setCellWidget(2 + i, 1, QLineEdit(''))
                i += 1

    def getParams(self):
        x = self.table.cellWidget(0, 1).value()
        y = self.table.cellWidget(1, 1).value()
        extra = None
        if self.tilename.currentData().extraTypes != None:
            i = 0
            extra = []
            for t in self.tilename.currentData().extraTypes:
                if t == 'bool':
                    extra.append(bool(self.table.cellWidget(2 + i, 1).text()))
                elif t == 'int':
                    extra.append(int(self.table.cellWidget(2 + i, 1).text()))
                elif t == 'float':
                    extra.append(float(self.table.cellWidget(2 + i, 1).text()))
                elif t == 'str':
                    extra.append(str(self.table.cellWidget(2 + i, 1).text()))
                else:
                    extra.append(str(self.table.cellWidget(2 + i, 1).text()))
                i += 1
        return (x, y, extra)

    def getTileParams(self):
        return self.output

    def onOk(self):
        self.output = (self.tilename.currentData(), self.getParams())
        self.ready = True
        self.accept()

    def onCancel(self):
        self.output = (self.tilename.currentData(), self.getParams())
        self.ready = True
        self.reject()
Exemple #8
0
class App(QMainWindow):
    def __init__(self, paths=None):
        super().__init__()

        self.stations: List[Station] = []
        self.svg: BytesIO = None
        self.freq = '1H'
        self.dates = None
        self.title = None

        self.setWindowTitle('SHEAR Weather Reporter')
        self.activateWindow()
        self.setAcceptDrops(True)
        self.paths = paths if type(paths) == list else []
        plotWidget = QWidget()
        plotWidgetLayout = QHBoxLayout()
        plotWidget.setLayout(plotWidgetLayout)
        self.plotWidget = QtSvg.QSvgWidget()
        self.plotWidget.setFixedWidth(800)
        self.plotWidget.setFixedHeight(500)
        plotWidgetLayout.addWidget(self.plotWidget)

        self.logosWidget = QWidget()
        layout = QHBoxLayout()
        self.logosWidget.setLayout(layout)

        self.logos = [
            os.path.join(os.path.dirname(__file__), 'img', image)
            for image in [
                'newcastle_university_logo.png',
                'uganda_red_cross_society_long_small.png',
                'shear_logo.png',
                'actogether_uganda_small.png',
                'makerere_university_logo_long_small.png',
            ]
        ]

        for image in self.logos:

            logo = QLabel()
            logo.setPixmap(QtGui.QPixmap(image))
            layout.addWidget(logo)
            logo.setAlignment(QtCore.Qt.AlignCenter)

        self.saveButton = QPushButton('Save')
        self.saveButton.clicked.connect(self.save)

        self.updateButton = QPushButton('Rename')
        self.updateButton.clicked.connect(self.rename_locations)

        self.mainLayout = QVBoxLayout()

        self.mainWidget = QWidget()
        self.mainWidget.setLayout(self.mainLayout)

        self.setCentralWidget(self.mainWidget)

        self.resampleDropDown = QComboBox()
        self.dateDropDown = QComboBox()
        self.durationDropDown = QComboBox()
        self.durationDropDown.activated.connect(self.set_duration)

        self.dropWidget = QLabel('Drop a Davis WeatherLink export file here')
        self.dropWidget.setStyleSheet(
            "margin:5px; border:1px dashed rgb(0, 0, 0); padding:10px")

        self.resampleDropDown.activated.connect(self.set_frequency)

        self.dateDropDown.activated.connect(self.update_plot)

        self.mainLayout.setAlignment(QtCore.Qt.AlignCenter)
        self.title_widget = QWidget()
        title_layout = QHBoxLayout()

        for widget in [
                self.resampleDropDown,
                QLabel('Weather Report for the'), self.durationDropDown,
                QLabel('of'), self.dateDropDown
        ]:
            title_layout.addWidget(widget)
            widget.setFont(QtGui.QFont("Times", 15, QtGui.QFont.Bold))

        title_layout.setAlignment(QtCore.Qt.AlignCenter)
        self.title_widget.setLayout(title_layout)
        self.mainLayout.addWidget(self.dropWidget)
        self.mainLayout.addWidget(self.title_widget)
        self.mainLayout.addWidget(plotWidget)
        buttons = QWidget()
        buttons_layout = QHBoxLayout()
        self.linkButton = QPushButton('View Code on GitHub')
        buttons_layout.addWidget(self.linkButton)
        self.linkButton.clicked.connect(
            lambda _: webbrowser.open('github.com/fmcclean/weather-reporter'))
        buttons_layout.addWidget(self.updateButton)
        buttons_layout.addWidget(self.saveButton)
        buttons.setLayout(buttons_layout)
        self.mainLayout.addWidget(buttons)
        self.mainLayout.addWidget(self.logosWidget)

        self.showWidgets(False)

        if len(self.paths) > 0:
            self.add_data()

    def update_location(self):
        if self.df is not None:
            self.update_plot()

    def showWidgets(self, show: bool):
        self.title_widget.setVisible(show)
        self.logosWidget.setVisible(show)
        self.linkButton.setVisible(show)
        self.updateButton.setVisible(show)
        self.plotWidget.setVisible(show)
        self.saveButton.setVisible(show)
        self.dropWidget.setVisible(not show)

    def update_plot(self):

        self.svg = BytesIO()
        f, axes = plt.subplots(len(self.stations), figsize=(9, 6), sharex=True)
        date = self.dateDropDown.currentData()

        end_index = self.dates.index.get_loc(date) + 1

        for ax, station in zip(axes.flat if len(self.paths) > 1 else [axes],
                               self.stations):

            if end_index >= len(self.dates):
                end_date = station.temp.index[-1]
            else:
                end_date = self.dates.index[end_index]

            try:
                temp = station.temp.loc[date:end_date]
            except KeyError:
                temp = station.temp.iloc[0:0]

            ax.plot(temp.index.start_time, temp.values, color='firebrick')

            ax.set_ylabel('Temperature (C)')

            ymin, ymax = ax.get_ylim()
            ymax = ymax + ymax - ymin
            ax.set_ylim(ymin, ymax)

            twinx = ax.twinx()

            twinx.invert_yaxis()

            try:
                rain = station.rain.loc[date:end_date].iloc[:-1]
            except KeyError:
                rain = station.rain.iloc[0:0]

            width = rain.index.end_time - rain.index.start_time

            twinx.bar(rain.index.start_time,
                      rain.values,
                      width=width,
                      align='edge')

            twinx.set_ylabel('Rainfall (mm)')

            ymax, ymin = twinx.get_ylim()
            ymax = ymax + ymax - ymin
            twinx.set_ylim(ymax, ymin)

            locator = mdates.AutoDateLocator()
            ax.xaxis.set_major_locator(locator)
            ax.xaxis.set_major_formatter(mdates.ConciseDateFormatter(locator))

            plt.tight_layout()

            self.title = '{} Weather Report for the {} of {}'.format(
                self.resampleDropDown.currentText(),
                self.durationDropDown.currentText(),
                self.dateDropDown.currentText())

            ax.set_title("{}".format(station.location))
            ax.patch.set_visible(False)
        f.patch.set_visible(False)

        f.savefig(self.svg, format='svg')
        self.svg.seek(0)
        plt.close(f)

        self.plotWidget.load(self.svg.read())
        self.svg.seek(0)

    def create_pdf(self, path):

        title_style = style['h1']
        title_style.alignment = 1

        doc = SimpleDocTemplate(path,
                                rightMargin=0,
                                leftMargin=0,
                                topMargin=0,
                                bottomMargin=0,
                                pagesize=landscape(A4))

        story = [Paragraph(self.title, style=title_style), svg2rlg(self.svg)]

        chart_style = TableStyle([('ALIGN', (0, 0), (-1, -1), 'CENTER'),
                                  ('VALIGN', (0, 0), (-1, -1), 'CENTER')])

        images = []

        for logo in self.logos:
            from reportlab.lib import utils
            from reportlab.lib.units import cm
            height = 0.5 * cm

            img = utils.ImageReader(logo)
            iw, ih = img.getSize()
            aspect = iw / ih

            images.append(Image(logo, width=height * aspect, height=height))

        story.append(
            Table([images],
                  colWidths=[150 for _ in self.logos],
                  rowHeights=[3],
                  style=chart_style))

        doc.build(story)

    def dragEnterEvent(self, event):
        if event.mimeData().hasText():
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):
        url = event.mimeData().urls()[0].toLocalFile()
        self.paths.append(url)
        try:
            self.add_data([url])
        except pd.errors.ParserError:
            self.paths.pop()
            msg = QMessageBox()
            msg.setText('Could not load from {}'.format(
                os.path.basename(self.path)))
            msg.exec_()
            return

    def add_data(self, paths=None):
        if paths is None:
            paths = self.paths

        for path in paths:
            self.stations.append(Station(path))

        duration = self.stations[0].df.index[-1].end_time - self.stations[
            0].df.index[0].start_time

        if duration > pd.Timedelta(hours=min_length):
            if self.resampleDropDown.findText('Hourly') == -1:
                self.resampleDropDown.addItem('Hourly', '1H')
            if self.durationDropDown.findText('Day') == -1:
                self.durationDropDown.addItem('Day', 'day')
        if duration > pd.Timedelta(days=min_length):
            if self.resampleDropDown.findText('Daily') == -1:
                self.resampleDropDown.addItem('Daily', '1D')
            if self.durationDropDown.findText('Week') == -1:
                self.durationDropDown.addItem('Week', 'week')
            if self.durationDropDown.findText('Month') == -1:
                self.durationDropDown.addItem('Month', 'month')
        if duration > pd.Timedelta(weeks=min_length):
            if self.resampleDropDown.findText('Weekly') == -1:
                self.resampleDropDown.addItem('Weekly', '1W')
        if duration > pd.Timedelta(days=31 * min_length):
            if self.resampleDropDown.findText('Monthly') == -1:
                self.resampleDropDown.addItem('Monthly', '1M')
            if self.durationDropDown.findText('Year') == -1:
                self.durationDropDown.addItem('Year', 'year')

        self.set_duration()
        self.set_frequency()

        self.update_plot()

        self.showWidgets(True)

    def set_duration(self):
        duration = self.durationDropDown.currentData()
        record_lengths = [station.record_length for station in self.stations]
        idx = record_lengths.index(max(record_lengths))
        periods = getattr(self.stations[idx].rain.index.to_series().dt,
                          duration)
        self.dates = periods[periods.diff() != 0]
        self.dateDropDown.clear()

        if duration == 'month':
            date_string = '{:%m/%Y}'
        elif duration == 'year':
            date_string = '{:%Y}'
        else:
            date_string = '{:%d/%m/%Y}'

        for date in self.dates.index:
            self.dateDropDown.addItem(date_string.format(date.start_time),
                                      date)

        self.update_plot()

    def save(self):
        dialog = QFileDialog.getSaveFileName(filter="PDF Files (*.pdf)")
        if dialog[0] != '':
            self.create_pdf(dialog[0])

    def set_frequency(self):

        freq = self.resampleDropDown.itemData(
            self.resampleDropDown.currentIndex())

        self.freq = freq
        for station in self.stations:
            station.resample(self.freq)

        self.update_plot()

    def rename_locations(self):
        dialog = QDialog()
        layout = QVBoxLayout()
        dialog.setLayout(layout)
        b = QPushButton('Update')
        for station in self.stations:
            row = QWidget(dialog)
            row_layout = QHBoxLayout()
            row_layout.addWidget(QLabel(station.path))
            edit = QLineEdit(station.location)
            row_layout.addWidget(edit)
            edit.textChanged.connect(station.rename_location)
            row.setLayout(row_layout)
            layout.addWidget(row)
        layout.addWidget(b)
        b.clicked.connect(dialog.close)
        dialog.exec_()
        self.update_plot()
Exemple #9
0
class HeapPluginForm(PluginForm):
    def __init__(self):
        super(HeapPluginForm, self).__init__()
        self.parent      = None
        self.config_path = None
        self.config      = None
        self.tracer      = None
        self.heap        = None
        self.cur_arena   = None # default: main_arena
        self.ptr_size    = get_arch_ptrsize()

    def OnCreate(self, form):
        self.parent = self.FormToPyQtWidget(form)     
        self.setup_gui()
        self.init_heap()
        self.populate_gui()

    def setup_gui(self):
        self.chunk_widget = ChunkWidget(self)
        self.tracer_tab = TracerWidget(self)
        self.arena_widget = ArenaWidget(self)
        self.bins_widget = BinsWidget(self)
        self.tcache_widget = TcacheWidget(self)
        self.magic_widget = MagicWidget(self)
        self.config_widget = ConfigWidget(self)

        self.tabs = QTabWidget()
        self.tabs.addTab(self.tracer_tab, "Tracer")
        self.tabs.addTab(self.arena_widget, "Arena")
        self.tabs.addTab(self.bins_widget, "Bins")
        self.tabs.addTab(self.tcache_widget, "Tcache")
        self.tabs.addTab(self.magic_widget, "Magic")
        self.tabs.addTab(self.config_widget, "Config")

        self.btn_reload = QPushButton("Reload info")
        icon = QtGui.QIcon(os.path.normpath(ICONS_DIR + '/refresh.png'))
        self.btn_reload.setIcon(icon)
        self.btn_reload.setFixedWidth(120)
        self.btn_reload.setEnabled(False)
        self.btn_reload.clicked.connect(self.reload_gui_info)

        self.cb_arenas = QComboBox()
        self.cb_arenas.setFixedWidth(150)
        self.cb_arenas.currentIndexChanged[int].connect(self.cb_arenas_changed)

        hbox_arenas = QHBoxLayout()        
        hbox_arenas.addWidget(QLabel('Switch arena: '))
        hbox_arenas.addWidget(self.cb_arenas)
        hbox_arenas.setContentsMargins(0, 0, 0, 0)

        self.arenas_widget = QWidget()
        self.arenas_widget.setLayout(hbox_arenas)
        self.arenas_widget.setVisible(False)

        self.txt_warning = QLabel()
        self.txt_warning.setStyleSheet("font-weight: bold; color: red")
        self.txt_warning.setVisible(False)

        hbox_top = QHBoxLayout()
        hbox_top.addWidget(self.btn_reload)
        hbox_top.addWidget(self.arenas_widget)
        hbox_top.addWidget(self.txt_warning)
        hbox_top.setContentsMargins(0, 0, 0, 0)
        hbox_top.addStretch(1)

        vbox_left_panel = QVBoxLayout()        
        vbox_left_panel.addLayout(hbox_top)
        vbox_left_panel.addWidget(self.tabs)
        vbox_left_panel.setContentsMargins(0, 0, 0, 0)

        left_panel = QWidget()
        left_panel.setLayout(vbox_left_panel)

        self.splitter = QSplitter(QtCore.Qt.Horizontal)
        self.splitter.addWidget(left_panel)
        self.splitter.addWidget(self.chunk_widget)
        self.splitter.setStretchFactor(0, 1)

        main_layout = QVBoxLayout()
        main_layout.addWidget(self.splitter)
        self.parent.setLayout(main_layout)

    def populate_gui(self):
        self.magic_widget.populate_libc_offsets()
        self.reload_gui_info()

    def reload_gui_info(self, from_arena_cb=False):
        if not self.heap:
            return

        if not self.heap.get_heap_base():
            self.show_warning('Heap not initialized')
            return

        self.hide_warning()
        self.arenas_widget.setVisible(True)

        if not from_arena_cb:
            self.populate_arenas()

        self.arena_widget.populate_table()
        self.tcache_widget.populate_table()
        self.bins_widget.populate_tables()

    def init_heap(self):
        try:            
            self.config_path = CONFIG_PATH
            self.config = HeapConfig(self.config_path)
            self.config_widget.load_config()

        except Exception as e:
            self.config = None
            self.show_warning('Please, update the config file')
            warning(str(e))
            return

        if not self.config.offsets:
            arch_bits = self.ptr_size * 8
            self.show_warning('Config: Libc offsets for %d bits not found.' % arch_bits)
            return

        try:
            current_libc_version = get_libc_version()
            if self.config.libc_version == current_libc_version or current_libc_version == None:
                self.heap = Heap(self.config)
                self.btn_reload.setEnabled(True)
                self.tabs.setTabEnabled(3, self.heap.tcache_enabled)
            else:
                self.show_warning('Config: glibc version mismatched: %s != %s' % \
                    (str(self.config.libc_version), str(current_libc_version)))

        except AttributeError:
            self.show_warning('Invalid config file content')
         

    def populate_arenas(self):
        old_arena = self.cur_arena
        self.cb_arenas.clear()

        for addr, arena in self.heap.arenas():
            if addr == self.heap.main_arena_addr:
                self.cb_arenas.addItem("main_arena", None)
            else:
                self.cb_arenas.addItem("0x%x" % addr, addr)

        idx = self.cb_arenas.findData(old_arena)
        if idx != -1:
            self.cb_arenas.setCurrentIndex(idx)

    def show_warning(self, txt):
        self.txt_warning.setText(txt)
        self.txt_warning.setVisible(True)

    def hide_warning(self):
        self.txt_warning.setVisible(False)

    def cb_arenas_changed(self, idx):
        self.cur_arena = self.cb_arenas.itemData(idx)
        self.reload_gui_info(True)

    def show_chunk_info(self, address):
        self.chunk_widget.show_chunk(address)

    def Show(self):
        return PluginForm.Show(self, PLUGNAME, options = (
            PluginForm.FORM_TAB | PluginForm.FORM_CLOSE_LATER
        ))
    
    def OnClose(self, form):
        if self.tracer:
            self.tracer.unhook()
            log("Tracer disabled")
        log("Form closed")
Exemple #10
0
class RegExpDialog(QDialog):
    MaxCaptures = 6

    def __init__(self, parent=None):
        super(RegExpDialog, self).__init__(parent)

        self.patternComboBox = QComboBox()
        self.patternComboBox.setEditable(True)
        self.patternComboBox.setSizePolicy(QSizePolicy.Expanding,
                QSizePolicy.Preferred)

        patternLabel = QLabel("&Pattern:")
        patternLabel.setBuddy(self.patternComboBox)

        self.escapedPatternLineEdit = QLineEdit()
        self.escapedPatternLineEdit.setReadOnly(True)
        palette = self.escapedPatternLineEdit.palette()
        palette.setBrush(QPalette.Base,
                palette.brush(QPalette.Disabled, QPalette.Base))
        self.escapedPatternLineEdit.setPalette(palette)

        escapedPatternLabel = QLabel("&Escaped Pattern:")
        escapedPatternLabel.setBuddy(self.escapedPatternLineEdit)

        self.syntaxComboBox = QComboBox()
        self.syntaxComboBox.addItem("Regular expression v1", QRegExp.RegExp)
        self.syntaxComboBox.addItem("Regular expression v2", QRegExp.RegExp2)
        self.syntaxComboBox.addItem("Wildcard", QRegExp.Wildcard)
        self.syntaxComboBox.addItem("Fixed string", QRegExp.FixedString)

        syntaxLabel = QLabel("&Pattern Syntax:")
        syntaxLabel.setBuddy(self.syntaxComboBox)

        self.textComboBox = QComboBox()
        self.textComboBox.setEditable(True)
        self.textComboBox.setSizePolicy(QSizePolicy.Expanding,
                QSizePolicy.Preferred)

        textLabel = QLabel("&Text:")
        textLabel.setBuddy(self.textComboBox)

        self.caseSensitiveCheckBox = QCheckBox("Case &Sensitive")
        self.caseSensitiveCheckBox.setChecked(True)
        self.minimalCheckBox = QCheckBox("&Minimal")

        indexLabel = QLabel("Index of Match:")
        self.indexEdit = QLineEdit()
        self.indexEdit.setReadOnly(True)

        matchedLengthLabel = QLabel("Matched Length:")
        self.matchedLengthEdit = QLineEdit()
        self.matchedLengthEdit.setReadOnly(True)

        self.captureLabels = []
        self.captureEdits = []
        for i in range(self.MaxCaptures):
            self.captureLabels.append(QLabel("Capture %d:" % i))
            self.captureEdits.append(QLineEdit())
            self.captureEdits[i].setReadOnly(True)
        self.captureLabels[0].setText("Match:")

        checkBoxLayout = QHBoxLayout()
        checkBoxLayout.addWidget(self.caseSensitiveCheckBox)
        checkBoxLayout.addWidget(self.minimalCheckBox)
        checkBoxLayout.addStretch(1)

        mainLayout = QGridLayout()
        mainLayout.addWidget(patternLabel, 0, 0)
        mainLayout.addWidget(self.patternComboBox, 0, 1)
        mainLayout.addWidget(escapedPatternLabel, 1, 0)
        mainLayout.addWidget(self.escapedPatternLineEdit, 1, 1)
        mainLayout.addWidget(syntaxLabel, 2, 0)
        mainLayout.addWidget(self.syntaxComboBox, 2, 1)
        mainLayout.addLayout(checkBoxLayout, 3, 0, 1, 2)
        mainLayout.addWidget(textLabel, 4, 0)
        mainLayout.addWidget(self.textComboBox, 4, 1)
        mainLayout.addWidget(indexLabel, 5, 0)
        mainLayout.addWidget(self.indexEdit, 5, 1)
        mainLayout.addWidget(matchedLengthLabel, 6, 0)
        mainLayout.addWidget(self.matchedLengthEdit, 6, 1)

        for i in range(self.MaxCaptures):
            mainLayout.addWidget(self.captureLabels[i], 7 + i, 0)
            mainLayout.addWidget(self.captureEdits[i], 7 + i, 1)
        self.setLayout(mainLayout)

        self.patternComboBox.editTextChanged.connect(self.refresh)
        self.textComboBox.editTextChanged.connect(self.refresh)
        self.caseSensitiveCheckBox.toggled.connect(self.refresh)
        self.minimalCheckBox.toggled.connect(self.refresh)
        self.syntaxComboBox.currentIndexChanged.connect(self.refresh)

        self.patternComboBox.addItem("[A-Za-z_]+([A-Za-z_0-9]*)")
        self.textComboBox.addItem("(10 + delta4)* 32")

        self.setWindowTitle("RegExp")
        self.setFixedHeight(self.sizeHint().height())
        self.refresh()

    def refresh(self):
        self.setUpdatesEnabled(False)

        pattern = self.patternComboBox.currentText()
        text = self.textComboBox.currentText()

        escaped = str(pattern)
        escaped.replace('\\', '\\\\')
        escaped.replace('"', '\\"')
        self.escapedPatternLineEdit.setText('"' + escaped + '"')

        rx = QRegExp(pattern)
        cs = Qt.CaseSensitive if self.caseSensitiveCheckBox.isChecked() else Qt.CaseInsensitive
        rx.setCaseSensitivity(cs)
        rx.setMinimal(self.minimalCheckBox.isChecked())
        syntax = self.syntaxComboBox.itemData(self.syntaxComboBox.currentIndex())
        rx.setPatternSyntax(syntax)

        palette = self.patternComboBox.palette()
        if rx.isValid():
            palette.setColor(QPalette.Text,
                    self.textComboBox.palette().color(QPalette.Text))
        else:
            palette.setColor(QPalette.Text, Qt.red)
        self.patternComboBox.setPalette(palette)

        self.indexEdit.setText(str(rx.indexIn(text)))
        self.matchedLengthEdit.setText(str(rx.matchedLength()))

        for i in range(self.MaxCaptures):
            self.captureLabels[i].setEnabled(i <= rx.captureCount())
            self.captureEdits[i].setEnabled(i <= rx.captureCount())
            self.captureEdits[i].setText(rx.cap(i))

        self.setUpdatesEnabled(True)
Exemple #11
0
class FileManager(QWidget, _HalWidgetBase):
    def __init__(self, parent=None):
        super(FileManager, self).__init__(parent)
        self.title = 'Qtvcp File System View'
        self.left = 10
        self.top = 10
        self.width = 640
        self.height = 480
        self.media_path = (os.path.join(os.path.expanduser('~'),
                                        'linuxcnc/nc_files'))
        user = os.path.split(os.path.expanduser('~'))[-1]
        self.user_path = (os.path.join('/media', user))
        self.currentPath = None
        self.currentFolder = None
        self.initUI()

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        pasteBox = QHBoxLayout()
        self.textLine = QLineEdit()
        self.textLine.setToolTip('Current Director/selected File')
        self.pasteButton = QToolButton()
        self.pasteButton.setEnabled(False)
        self.pasteButton.setText('Paste')
        self.pasteButton.setToolTip(
            'Copy file from copy path to current directory/file')
        self.pasteButton.clicked.connect(self.paste)
        self.pasteButton.hide()
        pasteBox.addWidget(self.textLine)
        pasteBox.addWidget(self.pasteButton)

        self.copyBox = QFrame()
        hbox = QHBoxLayout()
        hbox.setContentsMargins(0, 0, 0, 0)
        self.copyLine = QLineEdit()
        self.copyLine.setToolTip('File path to copy from, when pasting')
        self.copyButton = QToolButton()
        self.copyButton.setText('Copy')
        self.copyButton.setToolTip('Record current file as copy path')
        self.copyButton.clicked.connect(self.recordCopyPath)
        hbox.addWidget(self.copyButton)
        hbox.addWidget(self.copyLine)
        self.copyBox.setLayout(hbox)
        self.copyBox.hide()

        self.model = QFileSystemModel()
        self.model.setRootPath(QDir.currentPath())
        self.model.setFilter(QDir.AllDirs | QDir.NoDot | QDir.Files)
        self.model.setNameFilterDisables(False)
        self.model.rootPathChanged.connect(self.folderChanged)

        self.list = QListView()
        self.list.setModel(self.model)
        self.updateDirectoryView(self.media_path)
        self.list.resize(640, 480)
        self.list.clicked[QModelIndex].connect(self.listClicked)
        self.list.activated.connect(self._getPathActivated)
        self.list.setAlternatingRowColors(True)

        self.cb = QComboBox()
        self.cb.currentIndexChanged.connect(self.filterChanged)
        self.fillCombobox(INFO.PROGRAM_FILTERS_EXTENSIONS)
        self.cb.setMinimumHeight(30)
        self.cb.setSizePolicy(QSizePolicy(QSizePolicy.Fixed,
                                          QSizePolicy.Fixed))

        self.button2 = QToolButton()
        self.button2.setText('Media')
        self.button2.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        self.button2.setMinimumSize(60, 30)
        self.button2.setToolTip('Jump to Media directory')
        self.button2.clicked.connect(self.onJumpClicked)

        SettingMenu = QMenu(self)
        self.settingMenu = SettingMenu
        for i in ('Media', 'User'):
            axisButton = QAction(QIcon.fromTheme('user-home'), i, self)
            # weird lambda i=i to work around 'function closure'
            axisButton.triggered.connect(
                lambda state, i=i: self.jumpTriggered(i))
            SettingMenu.addAction(axisButton)
        self.button2.setMenu(SettingMenu)

        self.button3 = QToolButton()
        self.button3.setText('Add Jump')
        self.button3.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        self.button3.setMinimumSize(60, 30)
        self.button3.setToolTip('Add current directory to jump button list')
        self.button3.clicked.connect(self.onActionClicked)

        hbox = QHBoxLayout()
        hbox.addWidget(self.button2)
        hbox.addWidget(self.button3)
        hbox.insertStretch(2, stretch=0)
        hbox.addWidget(self.cb)

        windowLayout = QVBoxLayout()
        windowLayout.addLayout(pasteBox)
        windowLayout.addWidget(self.copyBox)
        windowLayout.addWidget(self.list)
        windowLayout.addLayout(hbox)
        self.setLayout(windowLayout)
        self.show()

    def _hal_init(self):
        if self.PREFS_:
            last_path = self.PREFS_.getpref('last_loaded_directory',
                                            self.media_path, str,
                                            'BOOK_KEEPING')
            self.updateDirectoryView(last_path)
            LOG.debug("lAST FILE PATH: {}".format(last_path))
        else:
            LOG.debug("lAST FILE PATH: {}".format(self.media_path))
            self.updateDirectoryView(self.media_path)

    #########################
    # callbacks
    #########################

    # add shown text and hidden filter data from the INI
    def fillCombobox(self, data):
        for i in data:
            self.cb.addItem(i[0], i[1])

    def folderChanged(self, data):
        self.currentFolder = data
        self.textLine.setText(data)

    def updateDirectoryView(self, path):
        self.list.setRootIndex(self.model.setRootPath(path))

    # retrieve selected filter (it's held as QT.userData)
    def filterChanged(self, index):
        userdata = self.cb.itemData(index)
        self.model.setNameFilters(userdata)

    def listClicked(self, index):
        # the signal passes the index of the clicked item
        dir_path = self.model.filePath(index)
        if self.model.fileInfo(index).isFile():
            self.currentPath = dir_path
            self.textLine.setText(self.currentPath)
            return
        root_index = self.model.setRootPath(dir_path)
        self.list.setRootIndex(root_index)

    def onUserClicked(self):
        self.showUserDir()

    def onMediaClicked(self):
        self.showMediaDir()

    def onJumpClicked(self):
        data = self.button2.text()
        if data == 'Media':
            self.showMediaDir()
        elif data == 'User':
            self.showUserDir()
        else:
            self.updateDirectoryView(self.button2.text())

    def jumpTriggered(self, data):
        if data == 'Media':
            self.button2.setText('{}'.format(data))
            self.button2.setToolTip('Jump to Media directory')
            self.showMediaDir()
        elif data == 'User':
            self.button2.setText('{}'.format(data))
            self.button2.setToolTip('Jump to User directory')
            self.showUserDir()
        else:
            self.button2.setText('{}'.format(data))
            self.button2.setToolTip('Jump to directory: {}'.format(data))
            self.updateDirectoryView(self.button2.text())

    def onActionClicked(self):
        i = self.currentFolder
        button = QAction(QIcon.fromTheme('user-home'), i, self)
        # weird lambda i=i to work around 'function closure'
        button.triggered.connect(lambda state, i=i: self.jumpTriggered(i))
        self.settingMenu.addAction(button)

    # get current selection and update the path
    # then if the path is good load it into linuxcnc
    # record it in the preference file if available
    def _getPathActivated(self):
        row = self.list.selectionModel().currentIndex()
        self.listClicked(row)

        fname = self.currentPath
        if fname is None:
            return
        if fname:
            self.load(fname)

    def recordCopyPath(self):
        data, isFile = self.getCurrentSelected()
        if isFile:
            self.copyLine.setText(os.path.normpath(data))
            self.pasteButton.setEnabled(True)
        else:
            self.copyLine.setText('')
            self.pasteButton.setEnabled(False)
            STATUS.emit('error', OPERATOR_ERROR,
                        'Can only copy a file, not a folder')

    def paste(self):
        res = self.copyFile(self.copyLine.text(), self.textLine.text())
        if res:
            self.copyLine.setText('')
            self.pasteButton.setEnabled(False)

    ########################
    # helper functions
    ########################

    def showCopyControls(self, state):
        if state:
            self.copyBox.show()
            self.pasteButton.show()
        else:
            self.copyBox.hide()
            self.pasteButton.hide()

    def showMediaDir(self):
        self.updateDirectoryView(self.user_path)

    def showUserDir(self):
        self.updateDirectoryView(self.media_path)

    def copyFile(self, s, d):
        try:
            shutil.copy(s, d)
            return True
        except Exception as e:
            LOG.error("Copy file error: {}".format(e))
            STATUS.emit('error', OPERATOR_ERROR,
                        "Copy file error: {}".format(e))
            return False

    # moves the selection up
    # used with MPG scrolling
    def up(self):
        self.select_row('up')

    # moves the selection down
    # used with MPG scrolling
    def down(self):
        self.select_row('down')

    def select_row(self, style='down'):
        style = style.lower()
        selectionModel = self.list.selectionModel()
        row = selectionModel.currentIndex().row()
        self.rows = self.model.rowCount(self.list.rootIndex())

        if style == 'last':
            row = self.rows
        elif style == 'up':
            if row > 0:
                row -= 1
            else:
                row = 0
        elif style == 'down':
            if row < self.rows:
                row += 1
            else:
                row = self.rows
        else:
            return
        top = self.model.index(row, 0, self.list.rootIndex())
        selectionModel.setCurrentIndex(
            top, QItemSelectionModel.Select | QItemSelectionModel.Rows)
        selection = QItemSelection(top, top)
        selectionModel.clearSelection()
        selectionModel.select(selection, QItemSelectionModel.Select)

    # returns the current highlighted (selected) path as well as
    # whether it's a file or not.
    def getCurrentSelected(self):
        selectionModel = self.list.selectionModel()
        index = selectionModel.currentIndex()
        dir_path = self.model.filePath(index)
        if self.model.fileInfo(index).isFile():
            return (dir_path, True)
        else:
            return (dir_path, False)

    # This can be class patched to do something else
    def load(self, fname=None):
        if fname is None:
            self._getPathActivated()
            return
        self.recordBookKeeping()
        ACTION.OPEN_PROGRAM(fname)
        STATUS.emit('update-machine-log', 'Loaded: ' + fname, 'TIME')

    # This can be class patched to do something else
    def recordBookKeeping(self):
        fname = self.currentPath
        if fname is None:
            return
        if self.PREFS_:
            self.PREFS_.putpref('last_loaded_directory', self.model.rootPath(),
                                str, 'BOOK_KEEPING')
            self.PREFS_.putpref('RecentPath_0', fname, str, 'BOOK_KEEPING')
Exemple #12
0
class AccountAddDialog(QDialog):
    def __init__(self, parent, icons, edit=False, username='', password='', api=''):
        QDialog.__init__(self, parent)
        self.edit = edit

        # Build UI
        layout = QVBoxLayout()

        formlayout = QFormLayout()
        self.lbl_username = QLabel('Username:'******'Password:'******'Request PIN')
        self.api_auth.setTextFormat(QtCore.Qt.RichText)
        self.api_auth.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction)
        self.api_auth.setOpenExternalLinks(True)
        pin_layout.addWidget(self.password)
        pin_layout.addWidget(self.api_auth)

        formlayout.addRow(QLabel('Site:'), self.api)
        formlayout.addRow(self.lbl_username, self.username)
        formlayout.addRow(self.lbl_password, pin_layout)

        bottombox = QDialogButtonBox()
        bottombox.addButton(QDialogButtonBox.Save)
        bottombox.addButton(QDialogButtonBox.Cancel)
        bottombox.accepted.connect(self.validate)
        bottombox.rejected.connect(self.reject)

        # Populate APIs
        for libname, lib in sorted(utils.available_libs.items()):
            self.api.addItem(icons[libname], lib[0], libname)

        if self.edit:
            self.username.setEnabled(False)
            self.api.setCurrentIndex(self.api.findData(api, QtCore.Qt.UserRole))
            self.api.setEnabled(False)

        # Finish layouts
        layout.addLayout(formlayout)
        layout.addWidget(bottombox)

        self.setLayout(layout)

    def validate(self):
        if len(self.username.text()) is 0:
            if len(self.password.text()) is 0:
                self._error('Please fill the credentials fields.')
            else:
                self._error('Please fill the username field.')
        elif len(self.password.text()) is 0:
            self._error('Please fill the password/PIN field.')
        else:
            self.accept()

    def s_refresh(self, index):
        if not self.edit:
            self.username.setText("")
            self.password.setText("")

        if pyqt_version is 5:
            apiname = self.api.itemData(index)
        else:
            apiname = str(self.api.itemData(index))
        api = utils.available_libs[apiname]
        if api[2] == utils.LOGIN_OAUTH:
            apiname = str(self.api.itemData(index))
            url = utils.available_libs[apiname][4]
            self.api_auth.setText( "<a href=\"{}\">Request PIN</a>".format(url) )
            self.api_auth.show()

            self.lbl_username.setText('Name:')
            self.lbl_password.setText('PIN:')
            self.password.setEchoMode(QLineEdit.Normal)
        else:
            self.lbl_username.setText('Username:'******'Password:'******'Error', msg, QMessageBox.Ok)

    @staticmethod
    def do(parent=None, icons=None, edit=False, username='', password='', api=''):
        dialog = AccountAddDialog(parent, icons, edit, username, password, api)
        result = dialog.exec_()

        if result == QDialog.Accepted:
            currentIndex = dialog.api.currentIndex()
            return (
                    str(dialog.username.text()),
                    str(dialog.password.text()),
                    str(dialog.api.itemData(currentIndex))
                   )
        else:
            return None
Exemple #13
0
class EditPointsDialog:
    def __init__(self, model, owner_object):
        self.model = model
        self.__owner_object = owner_object

        self.__changes = {}

        self.__window = QDialog()
        layout = QVBoxLayout()
        self.__window.setLayout(layout)

        self.__group_widget = QComboBox()
        self.__group_widget.addItem("- all -", None)
        for teacher in model.subject.teachers:
            for group in teacher.taught_groups:
                self.__group_widget.addItem(group.moodle_name, group)
        layout.addWidget(self.__group_widget)

        self.__points_widget = QTableWidget()
        self.__points_widget.setColumnCount(2)
        self.__points_widget.setHorizontalHeaderLabels(["Name", "Points"])
        layout.addWidget(self.__points_widget)

        button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                      | QDialogButtonBox.Cancel)
        button_box.accepted.connect(self.__window.accept)
        button_box.rejected.connect(self.__window.reject)

        layout.addWidget(button_box)

        self.__refresh_students()
        self.__group_widget.currentIndexChanged.connect(
            lambda *args: self.__refresh_students())
        self.__points_widget.itemChanged.connect(self.__cell_changed)

    def __refresh_students(self):
        self.__highlight_edit = False
        group = self.__group_widget.itemData(
            self.__group_widget.currentIndex())
        if group is None:
            students = self.model.subject.students
        else:
            students = list(group.get_students())

        self.__points_widget.setRowCount(len(students))
        for no, student in enumerate(students):
            name_cell = QTableWidgetItem(f"{student.name} {student.surname}")
            name_cell.setFlags(name_cell.flags() ^ Qt.ItemIsEditable)
            self.__points_widget.setItem(no, 0, name_cell)
            points = student.get_points_for(self.__owner_object)
            if points is not None:
                self.__points_widget.setItem(
                    no, 1, PointsTableItem(student, points.points))
            else:
                self.__points_widget.setItem(no, 1, PointsTableItem(student))
        self.__highlight_edit = True

    def __cell_changed(self, cell):
        if self.__highlight_edit and isinstance(cell, PointsTableItem):
            cell.setBackground(QBrush(QColor(255, 255, 0)))
            self.__group_widget.setEnabled(False)

            self.__changes[cell.student] = cell.points

    def run(self):
        if self.__window.exec() == QDialog.Accepted:
            for student, points in self.__changes.items():
                student.set_points_for(self.__owner_object, points)
            self.model.data_edited()
class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()

        self.proxyModel = MySortFilterProxyModel(self)
        self.proxyModel.setDynamicSortFilter(True)

        self.sourceView = QTreeView()
        self.sourceView.setRootIsDecorated(False)
        self.sourceView.setAlternatingRowColors(True)

        sourceLayout = QHBoxLayout()
        sourceLayout.addWidget(self.sourceView)
        sourceGroupBox = QGroupBox("Original Model")
        sourceGroupBox.setLayout(sourceLayout)

        self.filterCaseSensitivityCheckBox = QCheckBox("Case sensitive filter")
        self.filterCaseSensitivityCheckBox.setChecked(True)
        self.filterPatternLineEdit = QLineEdit()
        self.filterPatternLineEdit.setText("Grace|Sports")
        filterPatternLabel = QLabel("&Filter pattern:")
        filterPatternLabel.setBuddy(self.filterPatternLineEdit)
        self.filterSyntaxComboBox = QComboBox()
        self.filterSyntaxComboBox.addItem("Regular expression", QRegExp.RegExp)
        self.filterSyntaxComboBox.addItem("Wildcard", QRegExp.Wildcard)
        self.filterSyntaxComboBox.addItem("Fixed string", QRegExp.FixedString)
        self.fromDateEdit = QDateEdit()
        self.fromDateEdit.setDate(QDate(2006, 12, 22))
        self.fromDateEdit.setCalendarPopup(True)
        fromLabel = QLabel("F&rom:")
        fromLabel.setBuddy(self.fromDateEdit)
        self.toDateEdit = QDateEdit()
        self.toDateEdit.setDate(QDate(2007, 1, 5))
        self.toDateEdit.setCalendarPopup(True)
        toLabel = QLabel("&To:")
        toLabel.setBuddy(self.toDateEdit)

        self.filterPatternLineEdit.textChanged.connect(self.textFilterChanged)
        self.filterSyntaxComboBox.currentIndexChanged.connect(self.textFilterChanged)
        self.filterCaseSensitivityCheckBox.toggled.connect(self.textFilterChanged)
        self.fromDateEdit.dateChanged.connect(self.dateFilterChanged)
        self.toDateEdit.dateChanged.connect(self.dateFilterChanged)

        self.proxyView = QTreeView()
        self.proxyView.setRootIsDecorated(False)
        self.proxyView.setAlternatingRowColors(True)
        self.proxyView.setModel(self.proxyModel)
        self.proxyView.setSortingEnabled(True)
        self.proxyView.sortByColumn(1, Qt.AscendingOrder)

        self.textFilterChanged()
        self.dateFilterChanged()

        proxyLayout = QGridLayout()
        proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3)
        proxyLayout.addWidget(filterPatternLabel, 1, 0)
        proxyLayout.addWidget(self.filterPatternLineEdit, 1, 1)
        proxyLayout.addWidget(self.filterSyntaxComboBox, 1, 2)
        proxyLayout.addWidget(self.filterCaseSensitivityCheckBox, 2, 0, 1, 3)
        proxyLayout.addWidget(fromLabel, 3, 0)
        proxyLayout.addWidget(self.fromDateEdit, 3, 1, 1, 2)
        proxyLayout.addWidget(toLabel, 4, 0)
        proxyLayout.addWidget(self.toDateEdit, 4, 1, 1, 2)
        proxyGroupBox = QGroupBox("Sorted/Filtered Model")
        proxyGroupBox.setLayout(proxyLayout)

        mainLayout = QVBoxLayout()
        mainLayout.addWidget(sourceGroupBox)
        mainLayout.addWidget(proxyGroupBox)
        self.setLayout(mainLayout)

        self.setWindowTitle("Custom Sort/Filter Model")
        self.resize(500, 450)

    def setSourceModel(self, model):
        self.proxyModel.setSourceModel(model)
        self.sourceView.setModel(model)

    def textFilterChanged(self):
        syntax = QRegExp.PatternSyntax(
            self.filterSyntaxComboBox.itemData(
                self.filterSyntaxComboBox.currentIndex()))
        caseSensitivity = (
            self.filterCaseSensitivityCheckBox.isChecked()
            and Qt.CaseSensitive or Qt.CaseInsensitive)
        regExp = QRegExp(self.filterPatternLineEdit.text(), caseSensitivity, syntax)
        self.proxyModel.setFilterRegExp(regExp)

    def dateFilterChanged(self):
        self.proxyModel.setFilterMinimumDate(self.fromDateEdit.date())
        self.proxyModel.setFilterMaximumDate(self.toDateEdit.date())
Exemple #15
0
class StretchLayout(QFormLayout):
    """
    Layout that contains the actual stretch information
    """
    def __init__(self, parent, stretch, gdaldataset=None):
        QFormLayout.__init__(self)

        # the mode
        self.modeCombo = QComboBox(parent)
        for text, code in MODE_DATA:
            self.modeCombo.addItem(text, code)

        # callback so we can set the state of other items when changed
        self.modeCombo.currentIndexChanged.connect(self.modeChanged)

        self.rampCombo = QComboBox(parent)

        # make sure the pseudocolor has the extra ramps loaded
        try:
            pseudocolor.loadExtraRamps()
        except Exception as e:
            QMessageBox.critical(parent, MESSAGE_TITLE, str(e))

        # populate combo - sort by type
        for (name, display) in pseudocolor.getRampsForDisplay():
            self.rampCombo.addItem(display, name)

        self.modeLayout = QHBoxLayout()
        self.modeLayout.addWidget(self.modeCombo)
        self.modeLayout.addWidget(self.rampCombo)

        self.addRow("Mode", self.modeLayout)

        if gdaldataset is None:
            # we don't have a dateset - is a rule
            # create spin boxes for the bands
            self.createSpinBands(parent)
        else:
            # we have a dataset. create combo
            # boxes with the band names
            self.createComboBands(gdaldataset, parent)

        self.addRow("Bands", self.bandLayout)

        # create the combo for the type of stretch
        self.stretchLayout = QHBoxLayout()
        self.stretchCombo = QComboBox(parent)
        for text, code in STRETCH_DATA:
            self.stretchCombo.addItem(text, code)
        # callback so we can set the state of other items when changed
        self.stretchCombo.currentIndexChanged.connect(self.stretchChanged)

        self.stretchLayout.addWidget(self.stretchCombo)

        # create the spin boxes for the std devs or hist min and max
        self.stretchParam1 = QDoubleSpinBox(parent)
        self.stretchParam1.setDecimals(3)
        self.stretchParam1Stats = QCheckBox(parent)
        self.stretchParam1Stats.setText("Statistics Min")

        self.stretchParam1Stats.stateChanged.connect(self.param1StatsChanged)

        self.stretchParam2 = QDoubleSpinBox(parent)
        self.stretchParam2.setDecimals(3)
        self.stretchParam2Stats = QCheckBox(parent)
        self.stretchParam2Stats.setText("Statistics Max")

        self.stretchParam2Stats.stateChanged.connect(self.param2StatsChanged)

        self.stretchLayout.addWidget(self.stretchParam1)
        self.stretchLayout.addWidget(self.stretchParam1Stats)
        self.stretchLayout.addWidget(self.stretchParam2)
        self.stretchLayout.addWidget(self.stretchParam2Stats)

        self.addRow("Stretch", self.stretchLayout)

        # now for no data, background and NaN
        self.fixedColorLayout = QHBoxLayout()
        self.nodataLabel = QLabel(parent)
        self.nodataLabel.setText("No Data")
        self.fixedColorLayout.addWidget(self.nodataLabel)
        self.fixedColorLayout.setAlignment(self.nodataLabel, Qt.AlignRight)
        self.nodataButton = ColorButton(parent)
        self.fixedColorLayout.addWidget(self.nodataButton)

        self.backgroundLabel = QLabel(parent)
        self.backgroundLabel.setText("Background")
        self.fixedColorLayout.addWidget(self.backgroundLabel)
        self.fixedColorLayout.setAlignment(self.backgroundLabel, Qt.AlignRight)
        self.backgroundButton = ColorButton(parent)
        self.fixedColorLayout.addWidget(self.backgroundButton)

        self.NaNLabel = QLabel(parent)
        self.NaNLabel.setText("NaN")
        self.fixedColorLayout.addWidget(self.NaNLabel)
        self.fixedColorLayout.setAlignment(self.NaNLabel, Qt.AlignRight)
        self.NaNButton = ColorButton(parent)
        self.fixedColorLayout.addWidget(self.NaNButton)

        self.addRow("Fixed Colors", self.fixedColorLayout)

        # set state of GUI for this stretch
        self.updateStretch(stretch)

    def param1StatsChanged(self, state):
        """
        Called when the 'Statistics Min' box is checked
        """
        self.stretchParam1.setEnabled(state != Qt.Checked)

    def param2StatsChanged(self, state):
        """
        Called when the 'Statistics Max' box is checked
        """
        self.stretchParam2.setEnabled(state != Qt.Checked)

    def updateStretch(self, stretch):
        """
        Change the state of the GUI to match the given stretch
        """
        # the mode
        idx = self.modeCombo.findData(stretch.mode)
        if idx != -1:
            self.modeCombo.setCurrentIndex(idx)

        # ramp
        if stretch.rampName is not None:
            idx = self.rampCombo.findData(stretch.rampName)
            if idx != -1:
                self.rampCombo.setCurrentIndex(idx)

        # set ramp state depending on if we are pseudo color or not
        state = stretch.mode == viewerstretch.VIEWER_MODE_PSEUDOCOLOR
        self.rampCombo.setEnabled(state)

        # set the bands depending on if we are RGB or not
        if stretch.mode == viewerstretch.VIEWER_MODE_RGB:
            self.redWidget.setToolTip("Red")
            self.greenWidget.setToolTip("Green")
            self.blueWidget.setToolTip("Blue")
            (r, g, b) = stretch.bands
            if isinstance(self.redWidget, QSpinBox):
                self.redWidget.setValue(r)
                self.greenWidget.setValue(g)
                self.blueWidget.setValue(b)
            else:
                self.redWidget.setCurrentIndex(r - 1)
                self.greenWidget.setCurrentIndex(g - 1)
                self.blueWidget.setCurrentIndex(b - 1)

        else:
            self.redWidget.setToolTip("Displayed Band")
            self.greenWidget.setEnabled(False)
            self.blueWidget.setEnabled(False)

            if isinstance(self.redWidget, QSpinBox):
                self.redWidget.setValue(stretch.bands[0])
            else:
                self.redWidget.setCurrentIndex(stretch.bands[0] - 1)

        # stretch mode
        idx = self.stretchCombo.findData(stretch.stretchmode)
        if idx != -1:
            self.stretchCombo.setCurrentIndex(idx)

        state = stretch.mode != viewerstretch.VIEWER_MODE_COLORTABLE
        self.stretchCombo.setEnabled(state)

        if stretch.stretchmode == viewerstretch.VIEWER_STRETCHMODE_STDDEV:
            self.stretchParam2.setEnabled(False)
            self.stretchParam1Stats.setEnabled(False)
            self.stretchParam2Stats.setEnabled(False)
            self.stretchParam1Stats.setCheckState(Qt.Unchecked)
            self.stretchParam2Stats.setCheckState(Qt.Unchecked)
            self.stretchParam1.setRange(0, 10)
            self.stretchParam1.setSingleStep(0.1)
            self.stretchParam1.setValue(stretch.stretchparam[0])
            self.stretchParam1.setToolTip("Number of Standard Deviations")
        elif stretch.stretchmode == viewerstretch.VIEWER_STRETCHMODE_HIST:
            self.stretchParam1Stats.setEnabled(False)
            self.stretchParam2Stats.setEnabled(False)
            self.stretchParam1Stats.setCheckState(Qt.Unchecked)
            self.stretchParam2Stats.setCheckState(Qt.Unchecked)
            self.stretchParam1.setRange(0, 1)
            self.stretchParam1.setSingleStep(0.005)
            self.stretchParam1.setValue(stretch.stretchparam[0])
            self.stretchParam1.setToolTip("Minimum Proportion of Histogram")
            self.stretchParam2.setRange(0, 1)
            self.stretchParam2.setSingleStep(0.005)
            self.stretchParam2.setValue(stretch.stretchparam[1])
            self.stretchParam2.setToolTip("Maximum Proportion of Histogram")
        elif stretch.stretchmode == viewerstretch.VIEWER_STRETCHMODE_LINEAR:
            self.stretchParam1Stats.setEnabled(True)
            self.stretchParam2Stats.setEnabled(True)
            self.stretchParam1.setRange(-2**32, 2**32)
            self.stretchParam1.setSingleStep(1)

            if stretch.stretchparam[0] is None:
                self.stretchParam1Stats.setCheckState(Qt.Checked)
            else:
                self.stretchParam1.setValue(stretch.stretchparam[0])
                self.stretchParam1Stats.setCheckState(Qt.Unchecked)
            self.stretchParam1.setToolTip("Minimum Value")

            self.stretchParam2.setRange(-2**32, 2**32)
            self.stretchParam2.setSingleStep(1)

            if stretch.stretchparam[1] is None:
                self.stretchParam2Stats.setCheckState(Qt.Checked)
            else:
                self.stretchParam2.setValue(stretch.stretchparam[1])
                self.stretchParam2Stats.setCheckState(Qt.Unchecked)

            self.stretchParam2.setToolTip("Maximum Value")
        else:
            # no stretch
            self.stretchParam1.setEnabled(False)
            self.stretchParam2.setEnabled(False)
            self.stretchParam1Stats.setCheckState(Qt.Unchecked)
            self.stretchParam2Stats.setCheckState(Qt.Unchecked)
            self.stretchParam1Stats.setEnabled(False)
            self.stretchParam2Stats.setEnabled(False)
            self.stretchParam1.setToolTip("")
            self.stretchParam2.setToolTip("")

        # nodata etc
        self.nodataButton.setColorAsRGBATuple(stretch.nodata_rgba)
        self.backgroundButton.setColorAsRGBATuple(stretch.background_rgba)
        self.NaNButton.setColorAsRGBATuple(stretch.nan_rgba)

    def createSpinBands(self, parent):
        """
        For the case where we are creating a rule
        we have no band names so create spin boxes
        """
        # create the 3 band spin boxes
        self.bandLayout = QHBoxLayout()
        self.redWidget = QSpinBox(parent)
        self.redWidget.setRange(1, MAX_BAND_NUMBER)
        self.bandLayout.addWidget(self.redWidget)

        self.greenWidget = QSpinBox(parent)
        self.greenWidget.setRange(1, MAX_BAND_NUMBER)
        self.bandLayout.addWidget(self.greenWidget)

        self.blueWidget = QSpinBox(parent)
        self.blueWidget.setRange(1, MAX_BAND_NUMBER)
        self.bandLayout.addWidget(self.blueWidget)

    def createComboBands(self, gdaldataset, parent):
        """
        We have a dataset - create combo boxes with the band names
        """
        self.bandLayout = QHBoxLayout()
        self.redWidget = QComboBox(parent)
        self.bandLayout.addWidget(self.redWidget)

        self.greenWidget = QComboBox(parent)
        self.bandLayout.addWidget(self.greenWidget)

        self.blueWidget = QComboBox(parent)
        self.bandLayout.addWidget(self.blueWidget)

        self.populateComboFromDataset(self.redWidget, gdaldataset)
        self.populateComboFromDataset(self.greenWidget, gdaldataset)
        self.populateComboFromDataset(self.blueWidget, gdaldataset)

    def populateComboFromDataset(self, combo, gdaldataset):
        """
        Go through all the bands in the dataset and add a combo
        item for each one. Set the current index to the currentBand
        """
        for count in range(gdaldataset.RasterCount):
            bandnum = count + 1
            gdalband = gdaldataset.GetRasterBand(bandnum)
            name = gdalband.GetDescription()
            if name == '':
                # make up a name so the user can still choose
                name = 'Band %d' % bandnum
            combo.addItem(name, bandnum)

    @staticmethod
    def getBandValue(widget):
        """
        Depending on whether widget it a spinbox
        or a combo box extract the current value for it.
        """
        if isinstance(widget, QSpinBox):
            value = widget.value()
        else:
            index = widget.currentIndex()
            var = widget.itemData(index)
            value = var
        return value

    def getStretch(self):
        """
        Return a ViewerStretch object that reflects
        the current state of the GUI
        """
        obj = viewerstretch.ViewerStretch()
        index = self.modeCombo.currentIndex()
        obj.mode = self.modeCombo.itemData(index)

        bands = []
        value = self.getBandValue(self.redWidget)
        bands.append(value)
        if obj.mode == viewerstretch.VIEWER_MODE_RGB:
            value = self.getBandValue(self.greenWidget)
            bands.append(value)
            value = self.getBandValue(self.blueWidget)
            bands.append(value)
        obj.setBands(tuple(bands))

        if obj.mode == viewerstretch.VIEWER_MODE_PSEUDOCOLOR:
            idx = self.rampCombo.currentIndex()
            rampName = self.rampCombo.itemData(idx)
            obj.setPseudoColor(str(rampName))

        index = self.stretchCombo.currentIndex()
        obj.stretchmode = self.stretchCombo.itemData(index)
        if obj.stretchmode == viewerstretch.VIEWER_STRETCHMODE_STDDEV:
            value = self.stretchParam1.value()
            obj.setStdDevStretch(value)
        elif obj.stretchmode == viewerstretch.VIEWER_STRETCHMODE_HIST:
            histmin = self.stretchParam1.value()
            histmax = self.stretchParam2.value()
            obj.setHistStretch(histmin, histmax)
        elif obj.stretchmode == viewerstretch.VIEWER_STRETCHMODE_LINEAR:
            # need to do something cleverer here with the special value
            minVal = self.stretchParam1.value()
            if self.stretchParam1Stats.checkState() == Qt.Checked:
                minVal = None
            maxVal = self.stretchParam2.value()
            if self.stretchParam2Stats.checkState() == Qt.Checked:
                maxVal = None
            obj.setLinearStretch(minVal, maxVal)

        obj.setNoDataRGBA(self.nodataButton.getColorAsRGBATuple())
        obj.setBackgroundRGBA(self.backgroundButton.getColorAsRGBATuple())
        obj.setNaNRGBA(self.NaNButton.getColorAsRGBATuple())

        return obj

    def modeChanged(self, index):
        """
        Called when user changed the mode. 
        Updates other GUI elements as needed
        """
        mode = self.modeCombo.itemData(index)
        greenredEnabled = (mode == viewerstretch.VIEWER_MODE_RGB)
        self.greenWidget.setEnabled(greenredEnabled)
        self.blueWidget.setEnabled(greenredEnabled)
        if greenredEnabled:
            self.redWidget.setToolTip("Red")
            self.greenWidget.setToolTip("Green")
            self.blueWidget.setToolTip("Blue")
        else:
            self.redWidget.setToolTip("Displayed Band")
            self.greenWidget.setToolTip("")
            self.blueWidget.setToolTip("")

        if mode == viewerstretch.VIEWER_MODE_COLORTABLE:
            # need to set stretch to none
            self.stretchCombo.setCurrentIndex(0)
        state = mode != viewerstretch.VIEWER_MODE_COLORTABLE
        self.stretchCombo.setEnabled(state)

        state = mode == viewerstretch.VIEWER_MODE_PSEUDOCOLOR
        self.rampCombo.setEnabled(state)

    def stretchChanged(self, index):
        """
        Called when user changed the stretch. 
        Updates other GUI elements as needed
        """
        stretchmode = self.stretchCombo.itemData(index)
        if stretchmode == viewerstretch.VIEWER_STRETCHMODE_STDDEV:
            self.stretchParam1.setEnabled(True)
            self.stretchParam2.setEnabled(False)
            self.stretchParam1Stats.setCheckState(Qt.Unchecked)
            self.stretchParam2Stats.setCheckState(Qt.Unchecked)
            self.stretchParam1Stats.setEnabled(False)
            self.stretchParam2Stats.setEnabled(False)
            self.stretchParam1.setRange(0, 10)
            self.stretchParam1.setSingleStep(0.1)
            # always set back to this default
            self.stretchParam1.setValue(viewerstretch.VIEWER_DEFAULT_STDDEV)
            self.stretchParam1.setToolTip("Number of Standard Deviations")
            self.stretchParam2.setToolTip("")
        elif stretchmode == viewerstretch.VIEWER_STRETCHMODE_HIST:
            self.stretchParam1.setEnabled(True)
            self.stretchParam2.setEnabled(True)
            self.stretchParam1Stats.setCheckState(Qt.Unchecked)
            self.stretchParam2Stats.setCheckState(Qt.Unchecked)
            self.stretchParam1Stats.setEnabled(False)
            self.stretchParam2Stats.setEnabled(False)
            self.stretchParam1.setRange(0, 1)
            self.stretchParam1.setSingleStep(0.005)
            self.stretchParam1.setToolTip("Minimum Proportion of Histogram")
            self.stretchParam2.setRange(0, 1)
            self.stretchParam2.setSingleStep(0.005)
            self.stretchParam2.setToolTip("Maximum Proportion of Histogram")
            # set back to these defaults
            self.stretchParam1.setValue(viewerstretch.VIEWER_DEFAULT_HISTMIN)
            self.stretchParam2.setValue(viewerstretch.VIEWER_DEFAULT_HISTMAX)
        elif stretchmode == viewerstretch.VIEWER_STRETCHMODE_LINEAR:
            self.stretchParam1.setEnabled(True)
            self.stretchParam2.setEnabled(True)
            self.stretchParam1Stats.setEnabled(True)
            self.stretchParam2Stats.setEnabled(True)
            self.stretchParam1.setRange(-2**32, 2**32)
            self.stretchParam1.setSingleStep(1)
            self.stretchParam1.setToolTip("Minimum Value")
            self.stretchParam2.setRange(-2**32, 2**32)
            self.stretchParam2.setSingleStep(1)
            self.stretchParam2.setToolTip("Maximum Value")
            self.stretchParam1.setValue(0)  # set back to these defaults
            self.stretchParam2.setValue(0)
            self.stretchParam1Stats.setCheckState(Qt.Checked)
            self.stretchParam2Stats.setCheckState(Qt.Checked)
        else:
            self.stretchParam1.setEnabled(False)
            self.stretchParam2.setEnabled(False)
            self.stretchParam1Stats.setCheckState(Qt.Unchecked)
            self.stretchParam2Stats.setCheckState(Qt.Unchecked)
            self.stretchParam1Stats.setEnabled(False)
            self.stretchParam2Stats.setEnabled(False)
            self.stretchParam1.setToolTip("")
            self.stretchParam2.setToolTip("")
Exemple #16
0
class FileManager(QWidget, _HalWidgetBase):
    def __init__(self, parent=None):
        super(FileManager, self).__init__(parent)
        self.title = 'PyQt5 file system view - pythonspot.com'
        self.left = 10
        self.top = 10
        self.width = 640
        self.height = 480
        self.default_path = (os.path.join(os.path.expanduser('~'),
                                          'linuxcnc/nc_files/examples'))
        self.user_path = (os.path.join('/media'))
        self.currentPath = None
        self.initUI()

    # add shown text and hidden filter data from the INI
    def fillCombobox(self, data):
        for i in data:
            self.cb.addItem(i[0], i[1])

    def initUI(self):
        self.setWindowTitle(self.title)
        self.setGeometry(self.left, self.top, self.width, self.height)

        self.model = QFileSystemModel()
        self.model.setRootPath(QDir.currentPath())
        self.model.setFilter(QDir.AllDirs | QDir.NoDot | QDir.Files)
        self.model.setNameFilterDisables(False)

        self.list = QListView()
        self.list.setModel(self.model)
        self.updateDirectoryView(self.default_path)
        self.list.setWindowTitle("Dir View")
        self.list.resize(640, 480)
        self.list.clicked[QModelIndex].connect(self.clicked)
        self.list.activated.connect(self._getPathActivated)
        #self.list.currentChanged = self.currentChanged
        self.list.setAlternatingRowColors(True)

        self.cb = QComboBox()
        self.cb.currentIndexChanged.connect(self.filterChanged)
        self.fillCombobox(INFO.PROGRAM_FILTERS_EXTENSIONS)
        #self.cb.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))

        self.button = QPushButton()
        self.button.setText('Media')
        self.button.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        self.button.setToolTip('Jump to Media directory')
        self.button.clicked.connect(self.onMediaClicked)

        self.button2 = QPushButton()
        self.button2.setText('User')
        self.button2.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        self.button2.setToolTip('Jump to linuxcnc directory')
        self.button2.clicked.connect(self.onUserClicked)

        hbox = QHBoxLayout()
        hbox.addWidget(self.button)
        hbox.addWidget(self.button2)
        hbox.addWidget(self.cb)

        windowLayout = QVBoxLayout()
        windowLayout.addWidget(self.list)
        windowLayout.addLayout(hbox)
        self.setLayout(windowLayout)
        self.show()

    # this could return the current/previous selected as it's selected.
    # need to uncomment monkey patch of self.list.currentChanged above
    # so far this is not needed
    def currentChanged(self, c, p):
        dir_path = self.model.filePath(c)

    def updateDirectoryView(self, path):
        self.list.setRootIndex(self.model.setRootPath(path))

    # retrieve selected filter (it's held as QT.userData)
    def filterChanged(self, index):
        userdata = self.cb.itemData(index)
        self.model.setNameFilters(userdata)

    def clicked(self, index):
        # the signal passes the index of the clicked item
        dir_path = self.model.filePath(index)
        if self.model.fileInfo(index).isFile():
            self.currentPath = dir_path
            return
        root_index = self.model.setRootPath(dir_path)
        self.list.setRootIndex(root_index)

    def onMediaClicked(self):
        self.updateDirectoryView(self.user_path)

    def onUserClicked(self):
        self.updateDirectoryView(self.default_path)

    def select_row(self, style):
        style = style.lower()
        selectionModel = self.list.selectionModel()
        row = selectionModel.currentIndex().row()
        self.rows = self.model.rowCount(self.list.rootIndex())

        if style == 'last':
            row = self.rows
        elif style == 'up':
            if row > 0:
                row -= 1
            else:
                row = 0
        elif style == 'down':
            if row < self.rows:
                row += 1
            else:
                row = self.rows
        else:
            return
        top = self.model.index(row, 0, self.list.rootIndex())
        selectionModel.setCurrentIndex(
            top, QItemSelectionModel.Select | QItemSelectionModel.Rows)
        selection = QItemSelection(top, top)
        selectionModel.clearSelection()
        selectionModel.select(selection, QItemSelectionModel.Select)

    # returns the current highlighted (selected) path as well as
    # whether it's a file or not.
    def getCurrentSelected(self):
        selectionModel = self.list.selectionModel()
        index = selectionModel.currentIndex()
        dir_path = self.model.filePath(index)
        if self.model.fileInfo(index).isFile():
            return (dir_path, True)
        else:
            return (dir_path, False)

    def _hal_init(self):
        if self.PREFS_:
            last_path = self.PREFS_.getpref('last_loaded_directory',
                                            self.default_path, str,
                                            'BOOK_KEEPING')
            self.updateDirectoryView(last_path)
            LOG.debug("lAST FILE PATH: {}".format(last_path))
        else:
            LOG.debug("lAST FILE PATH: {}".format(self.default_path))
            self.updateDirectoryView(self.default_path)

    # get current selection and update the path
    # then if the path is good load it into linuxcnc
    # record it in the preference file if available
    def _getPathActivated(self):
        row = self.list.selectionModel().currentIndex()
        self.clicked(row)

        fname = self.currentPath
        if fname is None:
            return
        if fname:
            self.load(fname)

    # this can be class patched to do something else
    def load(self, fname=None):
        if fname is None:
            self._getPathActivated()
            return
        self.recordBookKeeping()
        ACTION.OPEN_PROGRAM(fname)
        STATUS.emit('update-machine-log', 'Loaded: ' + fname, 'TIME')

    # this can be class patched to do something else
    def recordBookKeeping(self):
        fname = self.currentPath
        if fname is None:
            return
        if self.PREFS_:
            self.PREFS_.putpref('last_loaded_directory', self.model.rootPath(),
                                str, 'BOOK_KEEPING')
            self.PREFS_.putpref('RecentPath_0', fname, str, 'BOOK_KEEPING')

    # moves the selection up
    # used with MPG scrolling
    def up(self):
        self.select_row('up')

    # moves the selection down
    # used with MPG scrolling
    def down(self):
        self.select_row('down')
class ProjectTreeColumn(QDialog):

    # Signalsnproject =
    dockWidget = pyqtSignal('PyQt_PyObject')
    undockWidget = pyqtSignal()
    changeTitle = pyqtSignal('PyQt_PyObject', 'QString')
    updateLocator = pyqtSignal()
    activeProjectChanged = pyqtSignal()

    def __init__(self, parent=None):
        super(ProjectTreeColumn, self).__init__(parent)
        vbox = QVBoxLayout(self)
        vbox.setSizeConstraint(QVBoxLayout.SetDefaultConstraint)
        vbox.setContentsMargins(0, 0, 0, 0)
        vbox.setSpacing(0)
        self._buttons = []
        frame = QFrame()
        frame.setObjectName("actionbar")
        box = QVBoxLayout(frame)
        box.setContentsMargins(1, 1, 1, 1)
        box.setSpacing(0)

        self._combo_project = QComboBox()
        self._combo_project.setSizePolicy(
            QSizePolicy.Expanding, QSizePolicy.Fixed)
        self._combo_project.setSizeAdjustPolicy(
            QComboBox.AdjustToMinimumContentsLengthWithIcon)
        self._combo_project.setObjectName("combo_projects")
        box.addWidget(self._combo_project)
        vbox.addWidget(frame)
        self._combo_project.setContextMenuPolicy(Qt.CustomContextMenu)
        self._projects_area = QStackedLayout()
        logger.debug("This is the projects area")
        vbox.addLayout(self._projects_area)

        # Empty widget
        self._empty_proj = QLabel(translations.TR_NO_PROJECTS)
        self._empty_proj.setAlignment(Qt.AlignCenter)
        self._empty_proj.setAutoFillBackground(True)
        self._empty_proj.setBackgroundRole(QPalette.Base)
        self._projects_area.addWidget(self._empty_proj)
        self._projects_area.setCurrentWidget(self._empty_proj)

        self.projects = []

        self._combo_project.activated.connect(
            self._change_current_project)
        self._combo_project.customContextMenuRequested[
            'const QPoint&'].connect(self.context_menu_for_root)

        connections = (
            {
                "target": "main_container",
                "signal_name": "addToProject",
                "slot": self._add_file_to_project
            },
            {
                "target": "main_container",
                "signal_name": "showFileInExplorer",
                "slot": self._show_file_in_explorer
            },
        )
        IDE.register_service('projects_explorer', self)
        IDE.register_signals('projects_explorer', connections)
        ExplorerContainer.register_tab(translations.TR_TAB_PROJECTS, self)

        # FIXME: Should have a ninja settings object that stores tree state
        # FIXME: Or bettter, application data object
        # TODO: check this:
        # self.connect(ide, SIGNAL("goingDown()"),
        #    self.tree_projects.shutdown)

        # def close_project_signal():
        #    self.emit(SIGNAL("updateLocator()"))

    def install_tab(self):
        ide = IDE.get_service('ide')
        ui_tools.install_shortcuts(self, actions.PROJECTS_TREE_ACTIONS, ide)
        ide.goingDown.connect(self._on_ide_going_down)

    def _on_ide_going_down(self):
        """Save some settings before close"""
        if self.current_tree is None:
            return
        ds = IDE.data_settings()
        show_filesize = not bool(self.current_tree.isColumnHidden(1))
        ds.setValue("projectsExplorer/showFileSize", show_filesize)

    def load_session_projects(self, projects):
        for project in projects:
            if os.path.exists(project):
                self._open_project_folder(project)

    def open_project_folder(self, folderName=None):
        if settings.WORKSPACE:
            directory = settings.WORKSPACE
        else:
            directory = os.path.expanduser("~")

        if folderName is None:
            folderName = QFileDialog.getExistingDirectory(
                self, translations.TR_OPEN_PROJECT_DIRECTORY, directory)
            logger.debug("Choosing Foldername")
        if folderName:
            if not file_manager.folder_exists(folderName):
                QMessageBox.information(
                    self,
                    translations.TR_PROJECT_NONEXIST_TITLE,
                    translations.TR_PROJECT_NONEXIST % folderName)
                return
            logger.debug("Opening %s" % folderName)
            for p in self.projects:
                if p.project.path == folderName:
                    QMessageBox.information(
                        self,
                        translations.TR_PROJECT_PATH_ALREADY_EXIST_TITLE,
                        translations.TR_PROJECT_PATH_ALREADY_EXIST
                        % folderName)
                    return
            self._open_project_folder(folderName)

    def _open_project_folder(self, folderName):
        ninjaide = IDE.get_service("ide")
        # TODO: handle exception when .nja file is empty
        project = NProject(folderName)
        qfsm = ninjaide.filesystem.open_project(project)
        if qfsm:
            self.add_project(project)
            self.save_recent_projects(folderName)
            # FIXME: show editor area?
            # main_container = IDE.get_service('main_container')
            # if main_container:
            #    main_container.show_editor_area()
            if len(self.projects) > 1:
                title = "%s (%s)" % (
                    translations.TR_TAB_PROJECTS, len(self.projects))
            else:
                title = translations.TR_TAB_PROJECTS
            self.changeTitle.emit(self, title)

    def _add_file_to_project(self, path):
        """Add the file for 'path' in the project the user choose here."""
        if self._projects_area.count() > 0:
            path_project = [self.current_project]
            _add_to_project = add_to_project.AddToProject(path_project, self)
            _add_to_project.exec_()
            if not _add_to_project.path_selected:
                return
            main_container = IDE.get_service('main_container')
            if not main_container:
                return
            editorWidget = main_container.get_current_editor()
            if not editorWidget.file_path:
                name = QInputDialog.getText(
                    None,
                    translations.TR_ADD_FILE_TO_PROJECT,
                    translations.TR_FILENAME + ": ")[0]
                if not name:
                    QMessageBox.information(
                        self,
                        translations.TR_INVALID_FILENAME,
                        translations.TR_INVALID_FILENAME_ENTER_A_FILENAME)
                    return
            else:
                name = file_manager.get_basename(editorWidget.file_path)
            new_path = file_manager.create_path(
                _add_to_project.path_selected, name)
            ide_srv = IDE.get_service("ide")
            old_file = ide_srv.get_or_create_nfile(path)
            new_file = old_file.save(editorWidget.text(), new_path)
            # FIXME: Make this file replace the original in the open tab
        else:
            pass
            # Message about no project

    def _show_file_in_explorer(self, path):
        '''Iterate through the list of available projects and show
        the current file in the explorer view for the first
        project that contains it (i.e. if the same file is
        included in multiple open projects, the path will be
        expanded for the first project only).
        Note: This slot is connected to the main container's
        "showFileInExplorer(QString)" signal.'''
        central = IDE.get_service('central_container')
        if central and not central.is_lateral_panel_visible():
            return
        for project in self.projects:
            index = project.model().index(path)
            if index.isValid():
                # This highlights the index in the tree for us
                project.scrollTo(index, QAbstractItemView.EnsureVisible)
                project.setCurrentIndex(index)
                break

    def add_project(self, project):
        if project not in self.projects:
            self._combo_project.addItem(project.name)
            tooltip = utils.path_with_tilde_homepath(project.path)
            self._combo_project.setToolTip(tooltip)
            index = self._combo_project.count() - 1
            self._combo_project.setItemData(index, project)
            ptree = TreeProjectsWidget(project)
            self._projects_area.addWidget(ptree)
            ptree.closeProject['PyQt_PyObject'].connect(self._close_project)
            pmodel = project.model
            ptree.setModel(pmodel)
            pindex = pmodel.index(pmodel.rootPath())
            ptree.setRootIndex(pindex)
            self.projects.append(ptree)
            self._projects_area.setCurrentWidget(ptree)  # Can be empty widget
            self._combo_project.setCurrentIndex(index)

        # FIXME: improve?
        # if len(self.projects) == 1:
        #     self._combo_project.currentIndexChanged[int].connect(
        #         self._change_current_project)

    def _close_project(self, widget):
        """Close the project related to the tree widget."""
        index = self._combo_project.currentIndex()
        self.projects.remove(widget)
        # index + 1 is necessary because the widget
        # with index 0 is the empty widget
        self._projects_area.takeAt(index + 1)
        self._combo_project.removeItem(index)
        index = self._combo_project.currentIndex()
        self._projects_area.setCurrentIndex(index + 1)
        ninjaide = IDE.get_service('ide')
        ninjaide.filesystem.close_project(widget.project.path)
        widget.deleteLater()
        if len(self.projects) > 1:
            title = "%s (%s)" % (
                translations.TR_TAB_PROJECTS, len(self.projects))
        else:
            title = translations.TR_TAB_PROJECTS
        self.changeTitle.emit(self, title)
        self.updateLocator.emit()

    def _change_current_project(self, index):
        nproject = self._combo_project.itemData(index)

        ninjaide = IDE.get_service("ide")
        projects = ninjaide.get_projects()
        for project in projects.values():
            if project == nproject:
                nproject.is_current = True
            else:
                project.is_current = False
        self._projects_area.setCurrentIndex(index + 1)
        self.activeProjectChanged.emit()

    def close_opened_projects(self):
        for project in reversed(self.projects):
            self._close_project(project)

    def save_project(self):
        """Save all the opened files that belongs to the actual project."""
        if self.current_project is not None:
            path = self.current_project.path
            main_container = IDE.get_service('main_container')
            if path and main_container:
                main_container.save_project(path)

    def create_new_project(self):
        wizard = new_project_manager.NewProjectManager(self)
        wizard.show()

    @property
    def current_project(self):
        project = None
        if self._projects_area.count() > 0 and self.current_tree is not None:
            project = self.current_tree.project
        return project

    @property
    def current_tree(self):
        tree = None
        widget = self._projects_area.currentWidget()
        if isinstance(widget, TreeProjectsWidget):
            tree = widget
        return tree

    def set_current_item(self, path):
        if self.current_project is not None:
            self.current_tree.set_current_item(path)

    def save_recent_projects(self, folder):
        settings = IDE.data_settings()
        recent_project_list = settings.value('recentProjects', {})
        # if already exist on the list update the date time
        projectProperties = json_manager.read_ninja_project(folder)
        name = projectProperties.get('name', '')
        description = projectProperties.get('description', '')

        if not name:
            name = file_manager.get_basename(folder)

        if not description:
            description = translations.TR_NO_DESCRIPTION

        if folder in recent_project_list:
            properties = recent_project_list[folder]
            properties["lastopen"] = QDateTime.currentDateTime()
            properties["name"] = name
            properties["description"] = description
            recent_project_list[folder] = properties
        else:
            recent_project_list[folder] = {
                "name": name,
                "description": description,
                "isFavorite": False, "lastopen": QDateTime.currentDateTime()}
            # if the length of the project list it's high that 10 then delete
            # the most old
            # TODO: add the length of available projects to setting
            if len(recent_project_list) > MAX_RECENT_PROJECTS:
                del recent_project_list[self.find_most_old_open(
                    recent_project_list)]
        settings.setValue('recentProjects', recent_project_list)

    def find_most_old_open(self, recent_project_list):
        listFounder = []
        for recent_project_path, content in list(recent_project_list.items()):
            listFounder.append((recent_project_path, int(
                content["lastopen"].toString("yyyyMMddHHmmzzz"))))
        listFounder = sorted(listFounder, key=lambda date: listFounder[1],
                             reverse=True)   # sort by date last used
        return listFounder[0][0]

    def reject(self):
        if self.parent() is None:
            self.dockWidget.emit(self)

    def closeEvent(self, event):
        self.dockWidget.emit(self)
        event.ignore()

    def context_menu_for_root(self):
        menu = QMenu(self)
        if self.current_tree is None:
            # No projects
            return
        path = self.current_tree.project.path
        # Reset index
        self.current_tree.setCurrentIndex(QModelIndex())

        action_add_file = menu.addAction(QIcon(":img/new"),
                                         translations.TR_ADD_NEW_FILE)
        action_add_folder = menu.addAction(QIcon(
            ":img/openProj"), translations.TR_ADD_NEW_FOLDER)
        action_create_init = menu.addAction(translations.TR_CREATE_INIT)
        menu.addSeparator()
        action_run_project = menu.addAction(translations.TR_RUN_PROJECT)
        action_properties = menu.addAction(translations.TR_PROJECT_PROPERTIES)
        action_show_file_size = menu.addAction(translations.TR_SHOW_FILESIZE)
        menu.addSeparator()
        action_close = menu.addAction(translations.TR_CLOSE_PROJECT)

        # Connections
        action_add_file.triggered.connect(
            lambda: self.current_tree._add_new_file(path))
        action_add_folder.triggered.connect(
            lambda: self.current_tree._add_new_folder(path))
        action_create_init.triggered.connect(self.current_tree._create_init)
        action_run_project.triggered.connect(
            self.current_tree._execute_project)
        action_properties.triggered.connect(
            self.current_tree.open_project_properties)
        action_close.triggered.connect(self.current_tree._close_project)
        action_show_file_size.triggered.connect(
            self.current_tree.show_filesize_info)

        # menu for the project
        for m in self.current_tree.extra_menus_by_scope['project']:
            if isinstance(m, QMenu):
                menu.addSeparator()
                menu.addMenu(m)

        # show the menu!
        menu.exec_(QCursor.pos())
    def __init__(self, parent: 'ElectrumWindow', config: 'SimpleConfig'):
        WindowModalDialog.__init__(self, parent, _('Preferences'))
        self.config = config
        self.window = parent
        self.need_restart = False
        self.fx = self.window.fx
        self.wallet = self.window.wallet

        vbox = QVBoxLayout()
        tabs = QTabWidget()
        gui_widgets = []
        fee_widgets = []
        tx_widgets = []
        oa_widgets = []
        services_widgets = []

        # language
        lang_help = _(
            'Select which language is used in the GUI (after restart).')
        lang_label = HelpLabel(_('Language') + ':', lang_help)
        lang_combo = QComboBox()
        lang_combo.addItems(list(languages.values()))
        lang_keys = list(languages.keys())
        lang_cur_setting = self.config.get("language", '')
        try:
            index = lang_keys.index(lang_cur_setting)
        except ValueError:  # not in list
            index = 0
        lang_combo.setCurrentIndex(index)
        if not self.config.is_modifiable('language'):
            for w in [lang_combo, lang_label]:
                w.setEnabled(False)

        def on_lang(x):
            lang_request = list(languages.keys())[lang_combo.currentIndex()]
            if lang_request != self.config.get('language'):
                self.config.set_key("language", lang_request, True)
                self.need_restart = True

        lang_combo.currentIndexChanged.connect(on_lang)
        gui_widgets.append((lang_label, lang_combo))

        nz_help = _(
            'Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"'
        )
        nz_label = HelpLabel(_('Zeros after decimal point') + ':', nz_help)
        nz = QSpinBox()
        nz.setMinimum(0)
        nz.setMaximum(self.window.decimal_point)
        nz.setValue(self.window.num_zeros)
        if not self.config.is_modifiable('num_zeros'):
            for w in [nz, nz_label]:
                w.setEnabled(False)

        def on_nz():
            value = nz.value()
            if self.window.num_zeros != value:
                self.window.num_zeros = value
                self.config.set_key('num_zeros', value, True)
                self.window.history_list.update()
                self.window.address_list.update()

        nz.valueChanged.connect(on_nz)
        gui_widgets.append((nz_label, nz))

        msg = '\n'.join([
            _('Time based: fee rate is based on average confirmation time estimates'
              ),
            _('Mempool based: fee rate is targeting a depth in the memory pool'
              )
        ])
        fee_type_label = HelpLabel(_('Fee estimation') + ':', msg)
        fee_type_combo = QComboBox()
        fee_type_combo.addItems([_('Static'), _('ETA'), _('Mempool')])
        fee_type_combo.setCurrentIndex((2 if self.config.use_mempool_fees(
        ) else 1) if self.config.is_dynfee() else 0)

        def on_fee_type(x):
            self.config.set_key('mempool_fees', x == 2)
            self.config.set_key('dynamic_fees', x > 0)

        fee_type_combo.currentIndexChanged.connect(on_fee_type)
        fee_widgets.append((fee_type_label, fee_type_combo))

        use_rbf = bool(self.config.get('use_rbf', True))
        use_rbf_cb = QCheckBox(_('Use Replace-By-Fee'))
        use_rbf_cb.setChecked(use_rbf)
        use_rbf_cb.setToolTip(
            _('If you check this box, your transactions will be marked as non-final,') + '\n' + \
            _('and you will have the possibility, while they are unconfirmed, to replace them with transactions that pay higher fees.') + '\n' + \
            _('Note that some merchants do not accept non-final transactions until they are confirmed.'))

        def on_use_rbf(x):
            self.config.set_key('use_rbf', bool(x))
            batch_rbf_cb.setEnabled(bool(x))

        use_rbf_cb.stateChanged.connect(on_use_rbf)
        fee_widgets.append((use_rbf_cb, None))

        batch_rbf_cb = QCheckBox(_('Batch RBF transactions'))
        batch_rbf_cb.setChecked(bool(self.config.get('batch_rbf', False)))
        batch_rbf_cb.setEnabled(use_rbf)
        batch_rbf_cb.setToolTip(
            _('If you check this box, your unconfirmed transactions will be consolidated into a single transaction.') + '\n' + \
            _('This will save fees.'))

        def on_batch_rbf(x):
            self.config.set_key('batch_rbf', bool(x))

        batch_rbf_cb.stateChanged.connect(on_batch_rbf)
        fee_widgets.append((batch_rbf_cb, None))

        # lightning
        lightning_widgets = []

        backup_help = _(
            """If you configure a backup directory, a backup of your wallet file will be saved everytime you create a new channel.\n\nA backup file cannot be used as a wallet; it can only be used to retrieve the funds locked in your channels, by requesting your channels to be force closed (using data loss protection).\n\nIf the remote node is online, they will force-close your channels when you open the backup file. Note that a backup is not strictly necessary for that; if the remote party is online but they cannot reach you because you lost your wallet file, they should eventually close your channels, and your funds should be sent to an address recoverable from your seed (using static_remotekey).\n\nIf the remote node is not online, you can use the backup file to force close your channels, but only at the risk of losing all your funds in the channel, because you will be broadcasting an old state."""
        )
        backup_dir = self.config.get('backup_dir')
        backup_dir_label = HelpLabel(_('Backup directory') + ':', backup_help)
        self.backup_dir_e = QPushButton(backup_dir)
        self.backup_dir_e.clicked.connect(self.select_backup_dir)
        lightning_widgets.append((backup_dir_label, self.backup_dir_e))

        help_persist = _(
            """If this option is checked, Electrum will persist as a daemon after
you close all your wallet windows. Your local watchtower will keep
running, and it will protect your channels even if your wallet is not
open. For this to work, your computer needs to be online regularly.""")
        persist_cb = QCheckBox(_("Run as daemon after the GUI is closed"))
        persist_cb.setToolTip(help_persist)
        persist_cb.setChecked(bool(self.config.get('persist_daemon', False)))

        def on_persist_checked(x):
            self.config.set_key('persist_daemon', bool(x))

        persist_cb.stateChanged.connect(on_persist_checked)
        lightning_widgets.append((persist_cb, None))

        help_remote_wt = _(
            """To use a remote watchtower, enter the corresponding URL here""")
        remote_wt_cb = QCheckBox(_("Use a remote watchtower"))
        remote_wt_cb.setToolTip(help_remote_wt)
        remote_wt_cb.setChecked(bool(self.config.get('use_watchtower', False)))

        def on_remote_wt_checked(x):
            self.config.set_key('use_watchtower', bool(x))
            self.watchtower_url_e.setEnabled(bool(x))

        remote_wt_cb.stateChanged.connect(on_remote_wt_checked)
        watchtower_url = self.config.get('watchtower_url')
        self.watchtower_url_e = QLineEdit(watchtower_url)
        self.watchtower_url_e.setEnabled(
            self.config.get('use_watchtower', False))

        def on_wt_url():
            url = self.watchtower_url_e.text() or None
            watchtower_url = self.config.set_key('watchtower_url', url)

        self.watchtower_url_e.editingFinished.connect(on_wt_url)
        lightning_widgets.append((remote_wt_cb, self.watchtower_url_e))

        msg = _('OpenAlias record, used to receive coins and to sign payment requests.') + '\n\n'\
              + _('The following alias providers are available:') + '\n'\
              + '\n'.join(['https://cryptoname.co/', 'http://xmr.link']) + '\n\n'\
              + 'For more information, see https://openalias.org'
        alias_label = HelpLabel(_('OpenAlias') + ':', msg)
        alias = self.config.get('alias', '')
        self.alias_e = QLineEdit(alias)
        self.set_alias_color()
        self.alias_e.editingFinished.connect(self.on_alias_edit)
        oa_widgets.append((alias_label, self.alias_e))

        # Services
        ssl_cert = self.config.get('ssl_certfile')
        ssl_cert_label = HelpLabel(
            _('SSL cert file') + ':',
            'certificate file, with intermediate certificates if needed')
        self.ssl_cert_e = QPushButton(ssl_cert)
        self.ssl_cert_e.clicked.connect(self.select_ssl_certfile)
        services_widgets.append((ssl_cert_label, self.ssl_cert_e))

        ssl_privkey = self.config.get('ssl_keyfile')
        ssl_privkey_label = HelpLabel(_('SSL key file') + ':', '')
        self.ssl_privkey_e = QPushButton(ssl_privkey)
        self.ssl_privkey_e.clicked.connect(self.select_ssl_privkey)
        services_widgets.append((ssl_privkey_label, self.ssl_privkey_e))

        ssl_domain_label = HelpLabel(_('SSL domain') + ':', '')
        self.ssl_domain_e = QLineEdit('')
        self.ssl_domain_e.setReadOnly(True)
        services_widgets.append((ssl_domain_label, self.ssl_domain_e))

        self.check_ssl_config()

        hostname = self.config.get('services_hostname', 'localhost')
        hostname_label = HelpLabel(
            _('Hostname') + ':', 'must match your SSL domain')
        self.hostname_e = QLineEdit(hostname)
        self.hostname_e.editingFinished.connect(self.on_hostname)
        services_widgets.append((hostname_label, self.hostname_e))

        payserver_cb = QCheckBox(_("Run PayServer"))
        payserver_cb.setToolTip("Configure a port")
        payserver_cb.setChecked(bool(self.config.get('run_payserver', False)))

        def on_payserver_checked(x):
            self.config.set_key('run_payserver', bool(x))
            self.payserver_port_e.setEnabled(bool(x))

        payserver_cb.stateChanged.connect(on_payserver_checked)
        payserver_port = self.config.get('payserver_port', 8002)
        self.payserver_port_e = QLineEdit(str(payserver_port))
        self.payserver_port_e.editingFinished.connect(self.on_payserver_port)
        self.payserver_port_e.setEnabled(
            self.config.get('run_payserver', False))
        services_widgets.append((payserver_cb, self.payserver_port_e))

        help_local_wt = _(
            """To run a watchtower, you must run Electrum on a machine
that is always connected to the internet. Configure a port if you want it to be public."""
        )
        local_wt_cb = QCheckBox(_("Run Watchtower"))
        local_wt_cb.setToolTip(help_local_wt)
        local_wt_cb.setChecked(bool(self.config.get('run_watchtower', False)))

        def on_local_wt_checked(x):
            self.config.set_key('run_watchtower', bool(x))
            self.local_wt_port_e.setEnabled(bool(x))

        local_wt_cb.stateChanged.connect(on_local_wt_checked)
        watchtower_port = self.config.get('watchtower_port', '')
        self.local_wt_port_e = QLineEdit(str(watchtower_port))
        self.local_wt_port_e.setEnabled(
            self.config.get('run_watchtower', False))
        self.local_wt_port_e.editingFinished.connect(self.on_watchtower_port)
        services_widgets.append((local_wt_cb, self.local_wt_port_e))

        # units
        units = base_units_list
        msg = (
            _('Base unit of your wallet.') +
            '\n1 QTUM = 1000 mQTUM. 1 mQTUM = 1000 bits. 1 bit = 100 sat.\n' +
            _('This setting affects the Send tab, and all balance related fields.'
              ))
        unit_label = HelpLabel(_('Base unit') + ':', msg)
        unit_combo = QComboBox()
        unit_combo.addItems(units)
        unit_combo.setCurrentIndex(units.index(self.window.base_unit()))

        def on_unit(x, nz):
            unit_result = units[unit_combo.currentIndex()]
            if self.window.base_unit() == unit_result:
                return
            edits = self.window.amount_e, self.window.receive_amount_e
            amounts = [edit.get_amount() for edit in edits]
            self.window.decimal_point = base_unit_name_to_decimal_point(
                unit_result)
            self.config.set_key('decimal_point', self.window.decimal_point,
                                True)
            nz.setMaximum(self.window.decimal_point)
            self.window.history_list.update()
            self.window.request_list.update()
            self.window.address_list.update()
            for edit, amount in zip(edits, amounts):
                edit.setAmount(amount)
            self.window.update_status()

        unit_combo.currentIndexChanged.connect(lambda x: on_unit(x, nz))
        gui_widgets.append((unit_label, unit_combo))

        system_cameras = qrscanner._find_system_cameras()
        qr_combo = QComboBox()
        qr_combo.addItem("Default", "default")
        for camera, device in system_cameras.items():
            qr_combo.addItem(camera, device)
        #combo.addItem("Manually specify a device", config.get("video_device"))
        index = qr_combo.findData(self.config.get("video_device"))
        qr_combo.setCurrentIndex(index)
        msg = _("Install the zbar package to enable this.")
        qr_label = HelpLabel(_('Video Device') + ':', msg)
        qr_combo.setEnabled(qrscanner.libzbar is not None)
        on_video_device = lambda x: self.config.set_key(
            "video_device", qr_combo.itemData(x), True)
        qr_combo.currentIndexChanged.connect(on_video_device)
        gui_widgets.append((qr_label, qr_combo))

        colortheme_combo = QComboBox()
        colortheme_combo.addItem(_('Light'), 'default')
        colortheme_combo.addItem(_('Dark'), 'dark')
        index = colortheme_combo.findData(
            self.config.get('qt_gui_color_theme', 'default'))
        colortheme_combo.setCurrentIndex(index)
        colortheme_label = QLabel(_('Color theme') + ':')

        def on_colortheme(x):
            self.config.set_key('qt_gui_color_theme',
                                colortheme_combo.itemData(x), True)
            self.need_restart = True

        colortheme_combo.currentIndexChanged.connect(on_colortheme)
        gui_widgets.append((colortheme_label, colortheme_combo))

        updatecheck_cb = QCheckBox(
            _("Automatically check for software updates"))
        updatecheck_cb.setChecked(bool(self.config.get('check_updates',
                                                       False)))

        def on_set_updatecheck(v):
            self.config.set_key('check_updates', v == Qt.Checked, save=True)

        updatecheck_cb.stateChanged.connect(on_set_updatecheck)
        gui_widgets.append((updatecheck_cb, None))

        filelogging_cb = QCheckBox(_("Write logs to file"))
        filelogging_cb.setChecked(bool(self.config.get('log_to_file', False)))

        def on_set_filelogging(v):
            self.config.set_key('log_to_file', v == Qt.Checked, save=True)
            self.need_restart = True

        filelogging_cb.stateChanged.connect(on_set_filelogging)
        filelogging_cb.setToolTip(
            _('Debug logs can be persisted to disk. These are useful for troubleshooting.'
              ))
        gui_widgets.append((filelogging_cb, None))

        preview_cb = QCheckBox(_('Advanced preview'))
        preview_cb.setChecked(bool(self.config.get('advanced_preview', False)))
        preview_cb.setToolTip(
            _("Open advanced transaction preview dialog when 'Pay' is clicked."
              ))

        def on_preview(x):
            self.config.set_key('advanced_preview', x == Qt.Checked)

        preview_cb.stateChanged.connect(on_preview)
        tx_widgets.append((preview_cb, None))

        usechange_cb = QCheckBox(_('Use change addresses'))
        usechange_cb.setChecked(self.window.wallet.use_change)
        if not self.config.is_modifiable('use_change'):
            usechange_cb.setEnabled(False)

        def on_usechange(x):
            usechange_result = x == Qt.Checked
            if self.window.wallet.use_change != usechange_result:
                self.window.wallet.use_change = usechange_result
                self.window.wallet.db.put('use_change',
                                          self.window.wallet.use_change)
                multiple_cb.setEnabled(self.window.wallet.use_change)

        usechange_cb.stateChanged.connect(on_usechange)
        usechange_cb.setToolTip(
            _('Using change addresses makes it more difficult for other people to track your transactions.'
              ))
        tx_widgets.append((usechange_cb, None))

        def on_multiple(x):
            multiple = x == Qt.Checked
            if self.wallet.multiple_change != multiple:
                self.wallet.multiple_change = multiple
                self.wallet.db.put('multiple_change', multiple)

        multiple_change = self.wallet.multiple_change
        multiple_cb = QCheckBox(_('Use multiple change addresses'))
        multiple_cb.setEnabled(self.wallet.use_change)
        multiple_cb.setToolTip('\n'.join([
            _('In some cases, use up to 3 change addresses in order to break '
              'up large coin amounts and obfuscate the recipient address.'),
            _('This may result in higher transactions fees.')
        ]))
        multiple_cb.setChecked(multiple_change)
        multiple_cb.stateChanged.connect(on_multiple)
        tx_widgets.append((multiple_cb, None))

        def fmt_docs(key, klass):
            lines = [ln.lstrip(" ") for ln in klass.__doc__.split("\n")]
            return '\n'.join([key, "", " ".join(lines)])

        choosers = sorted(coinchooser.COIN_CHOOSERS.keys())
        if len(choosers) > 1:
            chooser_name = coinchooser.get_name(self.config)
            msg = _(
                'Choose coin (UTXO) selection method.  The following are available:\n\n'
            )
            msg += '\n\n'.join(
                fmt_docs(*item) for item in coinchooser.COIN_CHOOSERS.items())
            chooser_label = HelpLabel(_('Coin selection') + ':', msg)
            chooser_combo = QComboBox()
            chooser_combo.addItems(choosers)
            i = choosers.index(chooser_name) if chooser_name in choosers else 0
            chooser_combo.setCurrentIndex(i)

            def on_chooser(x):
                chooser_name = choosers[chooser_combo.currentIndex()]
                self.config.set_key('coin_chooser', chooser_name)

            chooser_combo.currentIndexChanged.connect(on_chooser)
            tx_widgets.append((chooser_label, chooser_combo))

        def on_unconf(x):
            self.config.set_key('confirmed_only', bool(x))

        conf_only = bool(self.config.get('confirmed_only', False))
        unconf_cb = QCheckBox(_('Spend only confirmed coins'))
        unconf_cb.setToolTip(_('Spend only confirmed inputs.'))
        unconf_cb.setChecked(conf_only)
        unconf_cb.stateChanged.connect(on_unconf)
        tx_widgets.append((unconf_cb, None))

        def on_outrounding(x):
            self.config.set_key('coin_chooser_output_rounding', bool(x))

        enable_outrounding = bool(
            self.config.get('coin_chooser_output_rounding', False))
        outrounding_cb = QCheckBox(_('Enable output value rounding'))
        outrounding_cb.setToolTip(
            _('Set the value of the change output so that it has similar precision to the other outputs.'
              ) + '\n' + _('This might improve your privacy somewhat.') +
            '\n' +
            _('If enabled, at most 100 satoshis might be lost due to this, per transaction.'
              ))
        outrounding_cb.setChecked(enable_outrounding)
        outrounding_cb.stateChanged.connect(on_outrounding)
        tx_widgets.append((outrounding_cb, None))

        block_explorers = sorted(util.block_explorer_info().keys())
        msg = _(
            'Choose which online block explorer to use for functions that open a web browser'
        )
        block_ex_label = HelpLabel(_('Online Block Explorer') + ':', msg)
        block_ex_combo = QComboBox()
        block_ex_combo.addItems(block_explorers)
        block_ex_combo.setCurrentIndex(
            block_ex_combo.findText(util.block_explorer(self.config)))

        def on_be(x):
            be_result = block_explorers[block_ex_combo.currentIndex()]
            self.config.set_key('block_explorer', be_result, True)

        block_ex_combo.currentIndexChanged.connect(on_be)
        tx_widgets.append((block_ex_label, block_ex_combo))

        # Fiat Currency
        hist_checkbox = QCheckBox()
        hist_capgains_checkbox = QCheckBox()
        fiat_address_checkbox = QCheckBox()
        ccy_combo = QComboBox()
        ex_combo = QComboBox()

        def update_currencies():
            if not self.window.fx: return
            currencies = sorted(
                self.fx.get_currencies(self.fx.get_history_config()))
            ccy_combo.clear()
            ccy_combo.addItems([_('None')] + currencies)
            if self.fx.is_enabled():
                ccy_combo.setCurrentIndex(
                    ccy_combo.findText(self.fx.get_currency()))

        def update_history_cb():
            if not self.fx: return
            hist_checkbox.setChecked(self.fx.get_history_config())
            hist_checkbox.setEnabled(self.fx.is_enabled())

        def update_fiat_address_cb():
            if not self.fx: return
            fiat_address_checkbox.setChecked(self.fx.get_fiat_address_config())

        def update_history_capgains_cb():
            if not self.fx: return
            hist_capgains_checkbox.setChecked(
                self.fx.get_history_capital_gains_config())
            hist_capgains_checkbox.setEnabled(hist_checkbox.isChecked())

        def update_exchanges():
            if not self.fx: return
            b = self.fx.is_enabled()
            ex_combo.setEnabled(b)
            if b:
                h = self.fx.get_history_config()
                c = self.fx.get_currency()
                exchanges = self.fx.get_exchanges_by_ccy(c, h)
            else:
                exchanges = self.fx.get_exchanges_by_ccy('USD', False)
            ex_combo.blockSignals(True)
            ex_combo.clear()
            ex_combo.addItems(sorted(exchanges))
            ex_combo.setCurrentIndex(
                ex_combo.findText(self.fx.config_exchange()))
            ex_combo.blockSignals(False)

        def on_currency(hh):
            if not self.fx: return
            b = bool(ccy_combo.currentIndex())
            ccy = str(ccy_combo.currentText()) if b else None
            self.fx.set_enabled(b)
            if b and ccy != self.fx.ccy:
                self.fx.set_currency(ccy)
            update_history_cb()
            update_exchanges()
            self.window.update_fiat()

        def on_exchange(idx):
            exchange = str(ex_combo.currentText())
            if self.fx and self.fx.is_enabled(
            ) and exchange and exchange != self.fx.exchange.name():
                self.fx.set_exchange(exchange)

        def on_history(checked):
            if not self.fx: return
            self.fx.set_history_config(checked)
            update_exchanges()
            self.window.history_model.refresh('on_history')
            if self.fx.is_enabled() and checked:
                self.fx.trigger_update()
            update_history_capgains_cb()

        def on_history_capgains(checked):
            if not self.fx: return
            self.fx.set_history_capital_gains_config(checked)
            self.window.history_model.refresh('on_history_capgains')

        def on_fiat_address(checked):
            if not self.fx: return
            self.fx.set_fiat_address_config(checked)
            self.window.address_list.refresh_headers()
            self.window.address_list.update()

        update_currencies()
        update_history_cb()
        update_history_capgains_cb()
        update_fiat_address_cb()
        update_exchanges()
        ccy_combo.currentIndexChanged.connect(on_currency)
        hist_checkbox.stateChanged.connect(on_history)
        hist_capgains_checkbox.stateChanged.connect(on_history_capgains)
        fiat_address_checkbox.stateChanged.connect(on_fiat_address)
        ex_combo.currentIndexChanged.connect(on_exchange)

        fiat_widgets = []
        fiat_widgets.append((QLabel(_('Fiat currency')), ccy_combo))
        fiat_widgets.append((QLabel(_('Source')), ex_combo))
        fiat_widgets.append((QLabel(_('Show history rates')), hist_checkbox))
        fiat_widgets.append((QLabel(_('Show capital gains in history')),
                             hist_capgains_checkbox))
        fiat_widgets.append((QLabel(_('Show Fiat balance for addresses')),
                             fiat_address_checkbox))

        tabs_info = [
            (gui_widgets, _('General')),
            (fee_widgets, _('Fees')),
            (tx_widgets, _('Transactions')),
            (lightning_widgets, _('Lightning')),
            (fiat_widgets, _('Fiat')),
            (services_widgets, _('Services')),
            (oa_widgets, _('OpenAlias')),
        ]
        for widgets, name in tabs_info:
            tab = QWidget()
            tab_vbox = QVBoxLayout(tab)
            grid = QGridLayout()
            for a, b in widgets:
                i = grid.rowCount()
                if b:
                    if a:
                        grid.addWidget(a, i, 0)
                    grid.addWidget(b, i, 1)
                else:
                    grid.addWidget(a, i, 0, 1, 2)
            tab_vbox.addLayout(grid)
            tab_vbox.addStretch(1)
            tabs.addTab(tab, name)

        vbox.addWidget(tabs)
        vbox.addStretch(1)
        vbox.addLayout(Buttons(CloseButton(self)))
        self.setLayout(vbox)
Exemple #19
0
class MainGlyphWindow(QMainWindow):

    def __init__(self, glyph, parent=None):
        super().__init__(parent)

        menuBar = self.menuBar()
        fileMenu = QMenu("&File", self)
        fileMenu.addAction("E&xit", self.close, QKeySequence.Quit)
        menuBar.addMenu(fileMenu)
        editMenu = QMenu("&Edit", self)
        self._undoAction = editMenu.addAction(
            "&Undo", self.undo, QKeySequence.Undo)
        self._redoAction = editMenu.addAction(
            "&Redo", self.redo, QKeySequence.Redo)
        editMenu.addSeparator()
        # XXX
        action = editMenu.addAction("C&ut", self.cutOutlines, QKeySequence.Cut)
        action.setEnabled(False)
        self._copyAction = editMenu.addAction(
            "&Copy", self.copyOutlines, QKeySequence.Copy)
        editMenu.addAction("&Paste", self.pasteOutlines, QKeySequence.Paste)
        editMenu.addAction(
            "Select &All", self.selectAll, QKeySequence.SelectAll)
        editMenu.addAction("&Deselect", self.deselect, "Ctrl+D")
        menuBar.addMenu(editMenu)
        glyphMenu = QMenu("&Glyph", self)
        glyphMenu.addAction("&Next Glyph", lambda: self.glyphOffset(1), "End")
        glyphMenu.addAction(
            "&Previous Glyph", lambda: self.glyphOffset(-1), "Home")
        glyphMenu.addAction("&Go To…", self.changeGlyph, "G")
        glyphMenu.addSeparator()
        self._layerAction = glyphMenu.addAction(
            "&Layer Actions…", self.layerActions, "L")
        menuBar.addMenu(glyphMenu)

        # create tools and buttons toolBars
        self._tools = []
        self._toolsToolBar = QToolBar("Tools", self)
        self._toolsToolBar.setMovable(False)
        self._buttons = []
        self._buttonsToolBar = QToolBar("Buttons", self)
        self._buttonsToolBar.setMovable(False)
        self.addToolBar(self._toolsToolBar)
        self.addToolBar(self._buttonsToolBar)

        # http://www.setnode.com/blog/right-aligning-a-button-in-a-qtoolbar/
        self._layersToolBar = QToolBar("Layers", self)
        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self._currentLayerBox = QComboBox(self)
        self._currentLayerBox.currentIndexChanged.connect(
            self._layerChanged)
        self._layersToolBar.addWidget(spacer)
        self._layersToolBar.addWidget(self._currentLayerBox)
        self._layersToolBar.setContentsMargins(0, 0, 2, 0)
        self._layersToolBar.setMovable(False)
        self.addToolBar(self._layersToolBar)

        viewMenu = self.createPopupMenu()
        viewMenu.setTitle("View")
        viewMenu.addSeparator()
        action = viewMenu.addAction("Lock Toolbars", self.lockToolBars)
        action.setCheckable(True)
        action.setChecked(True)
        menuBar.addMenu(viewMenu)

        self.view = GlyphView(self)
        self.setGlyph(glyph)
        selectionTool = self.installTool(SelectionTool)
        selectionTool.trigger()
        self.installTool(PenTool)
        self.installTool(RulerTool)
        self.installTool(KnifeTool)
        self.installButton(RemoveOverlapButton)

        self.setCentralWidget(self.view.scrollArea())
        self.resize(900, 700)
        self.view.setFocus(True)

    # ----------
    # Menu items
    # ----------

    def glyphOffset(self, offset):
        currentGlyph = self.view.glyph()
        font = currentGlyph.font
        glyphOrder = font.glyphOrder
        # should be enforced in fontView already
        if not (glyphOrder and len(glyphOrder)):
            return
        index = glyphOrder.index(currentGlyph.name)
        newIndex = (index + offset) % len(glyphOrder)
        glyph = font[glyphOrder[newIndex]]
        self.setGlyph(glyph)

    def changeGlyph(self):
        glyph = self.view.glyph()
        newGlyph, ok = GotoDialog.getNewGlyph(self, glyph)
        if ok and newGlyph is not None:
            self.setGlyph(newGlyph)

    def layerActions(self):
        glyph = self.view.glyph()
        newLayer, action, ok = LayerActionsDialog.getLayerAndAction(
            self, glyph)
        if ok and newLayer is not None:
            # TODO: whole glyph for now, but consider selection too
            if not glyph.name in newLayer:
                newLayer.newGlyph(glyph.name)
            otherGlyph = newLayer[glyph.name]
            otherGlyph.disableNotifications()
            if action == "Swap":
                tempGlyph = TGlyph()
                otherGlyph.drawPoints(tempGlyph.getPointPen())
                tempGlyph.width = otherGlyph.width
                otherGlyph.clearContours()
            glyph.drawPoints(otherGlyph.getPointPen())
            otherGlyph.width = glyph.width
            if action != "Copy":
                glyph.disableNotifications()
                glyph.clearContours()
                if action == "Swap":
                    tempGlyph.drawPoints(glyph.getPointPen())
                    glyph.width = tempGlyph.width
                glyph.enableNotifications()
            otherGlyph.enableNotifications()

    def undo(self):
        glyph = self.view.glyph()
        glyph.undo()

    def redo(self):
        glyph = self.view.glyph()
        glyph.redo()

    def cutOutlines(self):
        pass

    def copyOutlines(self):
        glyph = self.view.glyph()
        clipboard = QApplication.clipboard()
        mimeData = QMimeData()
        copyGlyph = glyph.getRepresentation("defconQt.FilterSelection")
        mimeData.setData("application/x-defconQt-glyph-data",
                         pickle.dumps([copyGlyph.serialize(
                             blacklist=("name", "unicode")
                         )]))
        clipboard.setMimeData(mimeData)

    def pasteOutlines(self):
        glyph = self.view.glyph()
        clipboard = QApplication.clipboard()
        mimeData = clipboard.mimeData()
        if mimeData.hasFormat("application/x-defconQt-glyph-data"):
            data = pickle.loads(mimeData.data(
                "application/x-defconQt-glyph-data"))
            if len(data) == 1:
                pen = glyph.getPointPen()
                pasteGlyph = TGlyph()
                pasteGlyph.deserialize(data[0])
                # TODO: if we serialize selected state, we don't need to do
                # this
                pasteGlyph.selected = True
                if len(pasteGlyph) or len(pasteGlyph.components) or \
                        len(pasteGlyph.anchors):
                    glyph.prepareUndo()
                    pasteGlyph.drawPoints(pen)

    def selectAll(self):
        glyph = self.view.glyph()
        glyph.selected = True
        if not len(glyph):
            for component in glyph.components:
                component.selected = True

    def deselect(self):
        glyph = self.view.glyph()
        for anchor in glyph.anchors:
            anchor.selected = False
        for component in glyph.components:
            component.selected = False
        glyph.selected = False

    def lockToolBars(self):
        action = self.sender()
        movable = not action.isChecked()
        for toolBar in (
                self._toolsToolBar, self._buttonsToolBar, self._layersToolBar):
            toolBar.setMovable(movable)

    # --------------------------
    # Tools & buttons management
    # --------------------------

    def installTool(self, tool):
        action = self._toolsToolBar.addAction(
            QIcon(tool.iconPath), tool.name, self._setViewTool)
        action.setCheckable(True)
        num = len(self._tools)
        action.setData(num)
        action.setShortcut(QKeySequence(str(num + 1)))
        self._tools.append(tool(parent=self.view))
        return action

    def uninstallTool(self, tool):
        pass  # XXX

    def _setViewTool(self):
        action = self.sender()
        index = action.data()
        newTool = self._tools[index]
        if newTool == self.view.currentTool():
            action.setChecked(True)
            return
        ok = self.view.setCurrentTool(newTool)
        # if view did change tool, disable them all and enable the one we want
        # otherwise, just disable the tool that was clicked.
        # previously we used QActionGroup to have exclusive buttons, but doing
        # it manually allows us to NAK a button change.
        if ok:
            for act in self._toolsToolBar.actions():
                act.setChecked(False)
        action.setChecked(ok)

    def installButton(self, button):
        action = self._buttonsToolBar.addAction(
            QIcon(button.iconPath), button.name, self._buttonAction)
        action.setData(len(self._buttons))
        self._buttons.append(button(parent=self.view))
        return action

    def uninstallButton(self, button):
        pass  # XXX

    def _buttonAction(self):
        action = self.sender()
        index = action.data()
        button = self._buttons[index]
        button.clicked()

    # --------------------
    # Notification support
    # --------------------

    # glyph

    def _subscribeToGlyph(self, glyph):
        if glyph is not None:
            glyph.addObserver(self, "_glyphChanged", "Glyph.Changed")
            glyph.addObserver(self, "_glyphNameChanged", "Glyph.NameChanged")
            glyph.addObserver(
                self, "_glyphSelectionChanged", "Glyph.SelectionChanged")
            undoManager = glyph.undoManager
            undoManager.canUndoChanged.connect(self._undoAction.setEnabled)
            undoManager.canRedoChanged.connect(self._redoAction.setEnabled)
            self._subscribeToFontAndLayerSet(glyph.font)

    def _unsubscribeFromGlyph(self, glyph):
        if glyph is not None:
            glyph.removeObserver(self, "Glyph.Changed")
            glyph.removeObserver(self, "Glyph.NameChanged")
            glyph.removeObserver(self, "Glyph.SelectionChanged")
            undoManager = glyph.undoManager
            undoManager.canUndoChanged.disconnect(self._undoAction.setEnabled)
            undoManager.canRedoChanged.disconnect(self._redoAction.setEnabled)
            self._unsubscribeFromFontAndLayerSet(glyph.font)

    def _glyphChanged(self, notification):
        self.view.glyphChanged()

    def _glyphNameChanged(self, notification):
        glyph = self.view.glyph()
        self.setWindowTitle(glyph.name, glyph.font)

    def _glyphSelectionChanged(self, notification):
        self._updateSelection()
        self.view.glyphChanged()

    def _fontInfoChanged(self, notification):
        self.view.fontInfoChanged()
        glyph = self.view.glyph()
        self.setWindowTitle(glyph.name, glyph.font)

    # layers & font

    def _subscribeToFontAndLayerSet(self, font):
        """Note: called by _subscribeToGlyph."""
        if font is None:
            return
        font.info.addObserver(self, "_fontInfoChanged", "Info.Changed")
        layerSet = font.layers
        if layerSet is None:
            return
        layerSet.addObserver(self, '_layerSetLayerDeleted',
                             'LayerSet.LayerDeleted')
        for event in ('LayerSet.LayerAdded', 'LayerSet.LayerChanged',
                      'LayerSet.LayerOrderChanged'):
            layerSet.addObserver(self, '_layerSetEvents', event)

    def _unsubscribeFromFontAndLayerSet(self, font):
        """Note: called by _unsubscribeFromGlyph."""
        if font is None:
            return
        font.info.removeObserver(self, "Info.Changed")
        layerSet = font.layers
        if layerSet is None:
            return
        for event in ('LayerSet.LayerAdded', 'LayerSet.LayerChanged',
                      'LayerSet.LayerOrderChanged', 'LayerSet.LayerDeleted'):
            layerSet.removeObserver(self, event)

    def _layerSetEvents(self, notification):
        self._updateLayerControls()

    def _layerSetLayerDeleted(self, notification):
        self._layerSetEvents(notification)
        self._currentLayerBox.setCurrentIndex(0)

    # other updaters

    def _updateUndoRedo(self):
        glyph = self.view.glyph()
        self._undoAction.setEnabled(glyph.canUndo())
        self._redoAction.setEnabled(glyph.canRedo())

    def _updateSelection(self):
        def hasSelection():
            glyph = self.view.glyph()
            for contour in glyph:
                if len(contour.selection):
                    return True
            for anchor in glyph.anchors:
                if anchor.selected:
                    return True
            for component in glyph.components:
                if component.selected:
                    return True
            return False
        self._copyAction.setEnabled(hasSelection())

    # --------------
    # Public Methods
    # --------------

    def setGlyph(self, glyph):
        currentGlyph = self.view.glyph()
        self._unsubscribeFromGlyph(currentGlyph)
        self._subscribeToGlyph(glyph)
        self.view.setGlyph(glyph)
        self._updateLayerControls()
        self._updateUndoRedo()
        self._updateSelection()
        self.setWindowTitle(glyph.name, glyph.font)

    def setDrawingAttribute(self, attr, value, layerName=None):
        self.view.setDrawingAttribute(attr, value, layerName)

    def drawingAttribute(self, attr, layerName=None):
        return self.view.drawingAttribute(attr, layerName)

    # -----------------
    # Layers management
    # -----------------

    def _layerChanged(self, newLayerIndex):
        glyph = self.view.glyph()
        layer = self._currentLayerBox.itemData(newLayerIndex)
        if layer is None:
            layer = self._makeLayer()
            if layer is None:
                # restore comboBox to active index
                layerSet = glyph.layerSet
                index = layerSet.layerOrder.index(glyph.layer.name)
                self._setLayerBoxIndex(index)
                return

        if glyph.name in layer:
            newGlyph = layer[glyph.name]
        else:
            # TODO: make sure we mimic defcon ufo3 APIs for that
            newGlyph = self._makeLayerGlyph(layer, glyph)
        self.setGlyph(newGlyph)

        # setting the layer-glyph here
        app = QApplication.instance()
        app.setCurrentGlyph(newGlyph)

    def _makeLayer(self):
        # TODO: what with duplicate names?
        glyph = self.view.glyph()
        newLayerName, ok = AddLayerDialog.getNewLayerName(self)
        if ok:
            layerSet = glyph.layerSet
            # TODO: this should return the layer
            layerSet.newLayer(newLayerName)
            return layerSet[newLayerName]
        else:
            return None

    def _makeLayerGlyph(self, layer, currentGlyph):
        glyph = layer.newGlyph(currentGlyph.name)
        glyph.width = currentGlyph.width
        glyph.template = True
        return glyph

    def _updateLayerControls(self):
        comboBox = self._currentLayerBox
        glyph = self.view.glyph()
        comboBox.blockSignals(True)
        comboBox.clear()
        for layer in glyph.layerSet:
            comboBox.addItem(layer.name, layer)
        comboBox.setCurrentText(glyph.layer.name)
        comboBox.addItem("New layer…", None)
        comboBox.blockSignals(False)
        self._layerAction.setEnabled(len(glyph.layerSet) > 1)

    def _setLayerBoxIndex(self, index):
        comboBox = self._currentLayerBox
        comboBox.blockSignals(True)
        comboBox.setCurrentIndex(index)
        comboBox.blockSignals(False)

    # ---------------------
    # QMainWindow functions
    # ---------------------

    def event(self, event):
        if event.type() == QEvent.WindowActivate:
            app = QApplication.instance()
            app.setCurrentGlyph(self.view.glyph())
        return super().event(event)

    def closeEvent(self, event):
        glyph = self.view.glyph()
        self._unsubscribeFromGlyph(glyph)
        event.accept()

    def setWindowTitle(self, title, font=None):
        if font is not None:
            title = "%s – %s %s" % (
                title, font.info.familyName, font.info.styleName)
        super().setWindowTitle(title)
Exemple #20
0
class AudioTest(QMainWindow):

    PUSH_MODE_LABEL = "Enable push mode"
    PULL_MODE_LABEL = "Enable pull mode"
    SUSPEND_LABEL = "Suspend playback"
    RESUME_LABEL = "Resume playback"

    DurationSeconds = 1
    ToneSampleRateHz = 600
    DataSampleRateHz = 44100

    def __init__(self):
        super(AudioTest, self).__init__()

        self.m_device = QAudioDeviceInfo.defaultOutputDevice()
        self.m_output = None

        self.initializeWindow()
        self.initializeAudio()

    def initializeWindow(self):
        layout = QVBoxLayout()

        self.m_deviceBox = QComboBox(activated=self.deviceChanged)
        for deviceInfo in QAudioDeviceInfo.availableDevices(QAudio.AudioOutput):
            self.m_deviceBox.addItem(deviceInfo.deviceName(), deviceInfo)

        layout.addWidget(self.m_deviceBox)

        self.m_modeButton = QPushButton(clicked=self.toggleMode)
        self.m_modeButton.setText(self.PUSH_MODE_LABEL)

        layout.addWidget(self.m_modeButton)

        self.m_suspendResumeButton = QPushButton(
                clicked=self.toggleSuspendResume)
        self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)

        layout.addWidget(self.m_suspendResumeButton)

        volumeBox = QHBoxLayout()
        volumeLabel = QLabel("Volume:")
        self.m_volumeSlider = QSlider(Qt.Horizontal, minimum=0, maximum=100,
                singleStep=10, valueChanged=self.volumeChanged)
        volumeBox.addWidget(volumeLabel)
        volumeBox.addWidget(self.m_volumeSlider)

        layout.addLayout(volumeBox)

        window = QWidget()
        window.setLayout(layout)

        self.setCentralWidget(window)

    def initializeAudio(self):
        self.m_pullTimer = QTimer(self, timeout=self.pullTimerExpired)
        self.m_pullMode = True

        self.m_format = QAudioFormat()
        self.m_format.setSampleRate(self.DataSampleRateHz)
        self.m_format.setChannelCount(1)
        self.m_format.setSampleSize(16)
        self.m_format.setCodec('audio/pcm')
        self.m_format.setByteOrder(QAudioFormat.LittleEndian)
        self.m_format.setSampleType(QAudioFormat.SignedInt)

        info = QAudioDeviceInfo(QAudioDeviceInfo.defaultOutputDevice())
        if not info.isFormatSupported(self.m_format):
            qWarning("Default format not supported - trying to use nearest")
            self.m_format = info.nearestFormat(self.m_format)

        self.m_generator = Generator(self.m_format,
                self.DurationSeconds * 1000000, self.ToneSampleRateHz, self)

        self.createAudioOutput()

    def createAudioOutput(self):
        self.m_audioOutput = QAudioOutput(self.m_device, self.m_format)
        self.m_audioOutput.notify.connect(self.notified)
        self.m_audioOutput.stateChanged.connect(self.handleStateChanged)

        self.m_generator.start()
        self.m_audioOutput.start(self.m_generator)
        self.m_volumeSlider.setValue(self.m_audioOutput.volume() * 100)

    def deviceChanged(self, index):
        self.m_pullTimer.stop()
        self.m_generator.stop()
        self.m_audioOutput.stop()
        self.m_device = self.m_deviceBox.itemData(index)

        self.createAudioOutput()

    def volumeChanged(self, value):
        if self.m_audioOutput is not None:
            self.m_audioOutput.setVolume(value / 100.0)

    def notified(self):
        qWarning("bytesFree = %d, elapsedUSecs = %d, processedUSecs = %d" % (
                self.m_audioOutput.bytesFree(),
                self.m_audioOutput.elapsedUSecs(),
                self.m_audioOutput.processedUSecs()))

    def pullTimerExpired(self):
        if self.m_audioOutput is not None and self.m_audioOutput.state() != QAudio.StoppedState:
            chunks = self.m_audioOutput.bytesFree() // self.m_audioOutput.periodSize()
            for _ in range(chunks):
                data = self.m_generator.read(self.m_audioOutput.periodSize())
                if data is None or len(data) != self.m_audioOutput.periodSize():
                    break

                self.m_output.write(data)

    def toggleMode(self):
        self.m_pullTimer.stop()
        self.m_audioOutput.stop()

        if self.m_pullMode:
            self.m_modeButton.setText(self.PULL_MODE_LABEL)
            self.m_output = self.m_audioOutput.start()
            self.m_pullMode = False
            self.m_pullTimer.start(20)
        else:
            self.m_modeButton.setText(self.PUSH_MODE_LABEL)
            self.m_pullMode = True
            self.m_audioOutput.start(self.m_generator)

        self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)

    def toggleSuspendResume(self):
        if self.m_audioOutput.state() == QAudio.SuspendedState:
            qWarning("status: Suspended, resume()")
            self.m_audioOutput.resume()
            self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
        elif self.m_audioOutput.state() == QAudio.ActiveState:
            qWarning("status: Active, suspend()")
            self.m_audioOutput.suspend()
            self.m_suspendResumeButton.setText(self.RESUME_LABEL)
        elif self.m_audioOutput.state() == QAudio.StoppedState:
            qWarning("status: Stopped, resume()")
            self.m_audioOutput.resume()
            self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
        elif self.m_audioOutput.state() == QAudio.IdleState:
            qWarning("status: IdleState")

    stateMap = {
        QAudio.ActiveState: "ActiveState",
        QAudio.SuspendedState: "SuspendedState",
        QAudio.StoppedState: "StoppedState",
        QAudio.IdleState: "IdleState"}

    def handleStateChanged(self, state):
        qWarning("state = " + self.stateMap.get(state, "Unknown"))
Exemple #21
0
class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()

        self.renderArea = RenderArea()

        self.shapeComboBox = QComboBox()
        self.shapeComboBox.addItem("Polygon", RenderArea.Polygon)
        self.shapeComboBox.addItem("Rectangle", RenderArea.Rect)
        self.shapeComboBox.addItem("Rounded Rectangle", RenderArea.RoundedRect)
        self.shapeComboBox.addItem("Ellipse", RenderArea.Ellipse)
        self.shapeComboBox.addItem("Pie", RenderArea.Pie)
        self.shapeComboBox.addItem("Chord", RenderArea.Chord)
        self.shapeComboBox.addItem("Path", RenderArea.Path)
        self.shapeComboBox.addItem("Line", RenderArea.Line)
        self.shapeComboBox.addItem("Polyline", RenderArea.Polyline)
        self.shapeComboBox.addItem("Arc", RenderArea.Arc)
        self.shapeComboBox.addItem("Points", RenderArea.Points)
        self.shapeComboBox.addItem("Text", RenderArea.Text)
        self.shapeComboBox.addItem("Pixmap", RenderArea.Pixmap)

        shapeLabel = QLabel("&Shape:")
        shapeLabel.setBuddy(self.shapeComboBox)

        self.penWidthSpinBox = QSpinBox()
        self.penWidthSpinBox.setRange(0, 20)
        self.penWidthSpinBox.setSpecialValueText("0 (cosmetic pen)")

        penWidthLabel = QLabel("Pen &Width:")
        penWidthLabel.setBuddy(self.penWidthSpinBox)

        self.penStyleComboBox = QComboBox()
        self.penStyleComboBox.addItem("Solid", Qt.SolidLine)
        self.penStyleComboBox.addItem("Dash", Qt.DashLine)
        self.penStyleComboBox.addItem("Dot", Qt.DotLine)
        self.penStyleComboBox.addItem("Dash Dot", Qt.DashDotLine)
        self.penStyleComboBox.addItem("Dash Dot Dot", Qt.DashDotDotLine)
        self.penStyleComboBox.addItem("None", Qt.NoPen)

        penStyleLabel = QLabel("&Pen Style:")
        penStyleLabel.setBuddy(self.penStyleComboBox)

        self.penCapComboBox = QComboBox()
        self.penCapComboBox.addItem("Flat", Qt.FlatCap)
        self.penCapComboBox.addItem("Square", Qt.SquareCap)
        self.penCapComboBox.addItem("Round", Qt.RoundCap)

        penCapLabel = QLabel("Pen &Cap:")
        penCapLabel.setBuddy(self.penCapComboBox)

        self.penJoinComboBox = QComboBox()
        self.penJoinComboBox.addItem("Miter", Qt.MiterJoin)
        self.penJoinComboBox.addItem("Bevel", Qt.BevelJoin)
        self.penJoinComboBox.addItem("Round", Qt.RoundJoin)

        penJoinLabel = QLabel("Pen &Join:")
        penJoinLabel.setBuddy(self.penJoinComboBox)

        self.brushStyleComboBox = QComboBox()
        self.brushStyleComboBox.addItem("Linear Gradient",
                Qt.LinearGradientPattern)
        self.brushStyleComboBox.addItem("Radial Gradient",
                Qt.RadialGradientPattern)
        self.brushStyleComboBox.addItem("Conical Gradient",
                Qt.ConicalGradientPattern)
        self.brushStyleComboBox.addItem("Texture", Qt.TexturePattern)
        self.brushStyleComboBox.addItem("Solid", Qt.SolidPattern)
        self.brushStyleComboBox.addItem("Horizontal", Qt.HorPattern)
        self.brushStyleComboBox.addItem("Vertical", Qt.VerPattern)
        self.brushStyleComboBox.addItem("Cross", Qt.CrossPattern)
        self.brushStyleComboBox.addItem("Backward Diagonal", Qt.BDiagPattern)
        self.brushStyleComboBox.addItem("Forward Diagonal", Qt.FDiagPattern)
        self.brushStyleComboBox.addItem("Diagonal Cross", Qt.DiagCrossPattern)
        self.brushStyleComboBox.addItem("Dense 1", Qt.Dense1Pattern)
        self.brushStyleComboBox.addItem("Dense 2", Qt.Dense2Pattern)
        self.brushStyleComboBox.addItem("Dense 3", Qt.Dense3Pattern)
        self.brushStyleComboBox.addItem("Dense 4", Qt.Dense4Pattern)
        self.brushStyleComboBox.addItem("Dense 5", Qt.Dense5Pattern)
        self.brushStyleComboBox.addItem("Dense 6", Qt.Dense6Pattern)
        self.brushStyleComboBox.addItem("Dense 7", Qt.Dense7Pattern)
        self.brushStyleComboBox.addItem("None", Qt.NoBrush)

        brushStyleLabel = QLabel("&Brush Style:")
        brushStyleLabel.setBuddy(self.brushStyleComboBox)

        otherOptionsLabel = QLabel("Other Options:")
        self.antialiasingCheckBox = QCheckBox("&Antialiasing")
        self.transformationsCheckBox = QCheckBox("&Transformations")

        self.shapeComboBox.activated.connect(self.shapeChanged)
        self.penWidthSpinBox.valueChanged.connect(self.penChanged)
        self.penStyleComboBox.activated.connect(self.penChanged)
        self.penCapComboBox.activated.connect(self.penChanged)
        self.penJoinComboBox.activated.connect(self.penChanged)
        self.brushStyleComboBox.activated.connect(self.brushChanged)
        self.antialiasingCheckBox.toggled.connect(self.renderArea.setAntialiased)
        self.transformationsCheckBox.toggled.connect(self.renderArea.setTransformed)

        mainLayout = QGridLayout()
        mainLayout.setColumnStretch(0, 1)
        mainLayout.setColumnStretch(3, 1)
        mainLayout.addWidget(self.renderArea, 0, 0, 1, 4)
        mainLayout.setRowMinimumHeight(1, 6)
        mainLayout.addWidget(shapeLabel, 2, 1, Qt.AlignRight)
        mainLayout.addWidget(self.shapeComboBox, 2, 2)
        mainLayout.addWidget(penWidthLabel, 3, 1, Qt.AlignRight)
        mainLayout.addWidget(self.penWidthSpinBox, 3, 2)
        mainLayout.addWidget(penStyleLabel, 4, 1, Qt.AlignRight)
        mainLayout.addWidget(self.penStyleComboBox, 4, 2)
        mainLayout.addWidget(penCapLabel, 5, 1, Qt.AlignRight)
        mainLayout.addWidget(self.penCapComboBox, 5, 2)
        mainLayout.addWidget(penJoinLabel, 6, 1, Qt.AlignRight)
        mainLayout.addWidget(self.penJoinComboBox, 6, 2)
        mainLayout.addWidget(brushStyleLabel, 7, 1, Qt.AlignRight)
        mainLayout.addWidget(self.brushStyleComboBox, 7, 2)
        mainLayout.setRowMinimumHeight(8, 6)
        mainLayout.addWidget(otherOptionsLabel, 9, 1, Qt.AlignRight)
        mainLayout.addWidget(self.antialiasingCheckBox, 9, 2)
        mainLayout.addWidget(self.transformationsCheckBox, 10, 2)
        self.setLayout(mainLayout)

        self.shapeChanged()
        self.penChanged()
        self.brushChanged()
        self.antialiasingCheckBox.setChecked(True)

        self.setWindowTitle("Basic Drawing")

    def shapeChanged(self):
        shape = self.shapeComboBox.itemData(self.shapeComboBox.currentIndex(),
                IdRole)
        self.renderArea.setShape(shape)

    def penChanged(self):
        width = self.penWidthSpinBox.value()
        style = Qt.PenStyle(self.penStyleComboBox.itemData(
                self.penStyleComboBox.currentIndex(), IdRole))
        cap = Qt.PenCapStyle(self.penCapComboBox.itemData(
                self.penCapComboBox.currentIndex(), IdRole))
        join = Qt.PenJoinStyle(self.penJoinComboBox.itemData(
                self.penJoinComboBox.currentIndex(), IdRole))

        self.renderArea.setPen(QPen(Qt.blue, width, style, cap, join))

    def brushChanged(self):
        style = Qt.BrushStyle(self.brushStyleComboBox.itemData(
                self.brushStyleComboBox.currentIndex(), IdRole))

        if style == Qt.LinearGradientPattern:
            linearGradient = QLinearGradient(0, 0, 100, 100)
            linearGradient.setColorAt(0.0, Qt.white)
            linearGradient.setColorAt(0.2, Qt.green)
            linearGradient.setColorAt(1.0, Qt.black)
            self.renderArea.setBrush(QBrush(linearGradient))
        elif style == Qt.RadialGradientPattern:
            radialGradient = QRadialGradient(50, 50, 50, 70, 70)
            radialGradient.setColorAt(0.0, Qt.white)
            radialGradient.setColorAt(0.2, Qt.green)
            radialGradient.setColorAt(1.0, Qt.black)
            self.renderArea.setBrush(QBrush(radialGradient))
        elif style == Qt.ConicalGradientPattern:
            conicalGradient = QConicalGradient(50, 50, 150)
            conicalGradient.setColorAt(0.0, Qt.white)
            conicalGradient.setColorAt(0.2, Qt.green)
            conicalGradient.setColorAt(1.0, Qt.black)
            self.renderArea.setBrush(QBrush(conicalGradient))
        elif style == Qt.TexturePattern:
            self.renderArea.setBrush(QBrush(QPixmap(':/images/brick.png')))
        else:
            self.renderArea.setBrush(QBrush(Qt.green, style))
Exemple #22
0
class AudioTest(QMainWindow):

    PUSH_MODE_LABEL = "Enable push mode"
    PULL_MODE_LABEL = "Enable pull mode"
    SUSPEND_LABEL = "Suspend playback"
    RESUME_LABEL = "Resume playback"

    DurationSeconds = 1
    ToneSampleRateHz = 600
    DataSampleRateHz = 44100

    def __init__(self):
        super(AudioTest, self).__init__()

        self.m_device = QAudioDeviceInfo.defaultOutputDevice()
        self.m_output = None

        self.initializeWindow()
        self.initializeAudio()

    def initializeWindow(self):
        layout = QVBoxLayout()

        self.m_deviceBox = QComboBox(activated=self.deviceChanged)
        for deviceInfo in QAudioDeviceInfo.availableDevices(QAudio.AudioOutput):
            self.m_deviceBox.addItem(deviceInfo.deviceName(), deviceInfo)

        layout.addWidget(self.m_deviceBox)

        self.m_modeButton = QPushButton(clicked=self.toggleMode)
        self.m_modeButton.setText(self.PUSH_MODE_LABEL)

        layout.addWidget(self.m_modeButton)

        self.m_suspendResumeButton = QPushButton(
                clicked=self.toggleSuspendResume)
        self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)

        layout.addWidget(self.m_suspendResumeButton)

        volumeBox = QHBoxLayout()
        volumeLabel = QLabel("Volume:")
        self.m_volumeSlider = QSlider(Qt.Horizontal, minimum=0, maximum=100,
                singleStep=10, valueChanged=self.volumeChanged)
        volumeBox.addWidget(volumeLabel)
        volumeBox.addWidget(self.m_volumeSlider)

        layout.addLayout(volumeBox)

        window = QWidget()
        window.setLayout(layout)

        self.setCentralWidget(window)

    def initializeAudio(self):
        self.m_pullTimer = QTimer(self, timeout=self.pullTimerExpired)
        self.m_pullMode = True

        self.m_format = QAudioFormat()
        self.m_format.setSampleRate(self.DataSampleRateHz)
        self.m_format.setChannelCount(1)
        self.m_format.setSampleSize(16)
        self.m_format.setCodec('audio/pcm')
        self.m_format.setByteOrder(QAudioFormat.LittleEndian)
        self.m_format.setSampleType(QAudioFormat.SignedInt)

        info = QAudioDeviceInfo(QAudioDeviceInfo.defaultOutputDevice())
        if not info.isFormatSupported(self.m_format):
            qWarning("Default format not supported - trying to use nearest")
            self.m_format = info.nearestFormat(self.m_format)

        self.m_generator = Generator(self.m_format,
                self.DurationSeconds * 1000000, self.ToneSampleRateHz, self)

        self.createAudioOutput()

    def createAudioOutput(self):
        self.m_audioOutput = QAudioOutput(self.m_device, self.m_format)
        self.m_audioOutput.notify.connect(self.notified)
        self.m_audioOutput.stateChanged.connect(self.handleStateChanged)

        self.m_generator.start()
        self.m_audioOutput.start(self.m_generator)
        self.m_volumeSlider.setValue(self.m_audioOutput.volume() * 100)

    def deviceChanged(self, index):
        self.m_pullTimer.stop()
        self.m_generator.stop()
        self.m_audioOutput.stop()
        self.m_device = self.m_deviceBox.itemData(index)

        self.createAudioOutput()

    def volumeChanged(self, value):
        if self.m_audioOutput is not None:
            self.m_audioOutput.setVolume(value / 100.0)

    def notified(self):
        qWarning("bytesFree = %d, elapsedUSecs = %d, processedUSecs = %d" % (
                self.m_audioOutput.bytesFree(),
                self.m_audioOutput.elapsedUSecs(),
                self.m_audioOutput.processedUSecs()))

    def pullTimerExpired(self):
        if self.m_audioOutput is not None and self.m_audioOutput.state() != QAudio.StoppedState:
            chunks = self.m_audioOutput.bytesFree() // self.m_audioOutput.periodSize()
            for _ in range(chunks):
                data = self.m_generator.read(self.m_audioOutput.periodSize())
                if data is None or len(data) != self.m_audioOutput.periodSize():
                    break

                self.m_output.write(data)

    def toggleMode(self):
        self.m_pullTimer.stop()
        self.m_audioOutput.stop()

        if self.m_pullMode:
            self.m_modeButton.setText(self.PULL_MODE_LABEL)
            self.m_output = self.m_audioOutput.start()
            self.m_pullMode = False
            self.m_pullTimer.start(20)
        else:
            self.m_modeButton.setText(self.PUSH_MODE_LABEL)
            self.m_pullMode = True
            self.m_audioOutput.start(self.m_generator)

        self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)

    def toggleSuspendResume(self):
        if self.m_audioOutput.state() == QAudio.SuspendedState:
            qWarning("status: Suspended, resume()")
            self.m_audioOutput.resume()
            self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
        elif self.m_audioOutput.state() == QAudio.ActiveState:
            qWarning("status: Active, suspend()")
            self.m_audioOutput.suspend()
            self.m_suspendResumeButton.setText(self.RESUME_LABEL)
        elif self.m_audioOutput.state() == QAudio.StoppedState:
            qWarning("status: Stopped, resume()")
            self.m_audioOutput.resume()
            self.m_suspendResumeButton.setText(self.SUSPEND_LABEL)
        elif self.m_audioOutput.state() == QAudio.IdleState:
            qWarning("status: IdleState")

    stateMap = {
        QAudio.ActiveState: "ActiveState",
        QAudio.SuspendedState: "SuspendedState",
        QAudio.StoppedState: "StoppedState",
        QAudio.IdleState: "IdleState"}

    def handleStateChanged(self, state):
        qWarning("state = " + self.stateMap.get(state, "Unknown"))
class SelectParameterWidget(GenericParameterWidget):
    """Widget class for List parameter."""
    def __init__(self, parameter, parent=None):
        """Constructor

        :param parameter: A ListParameter object.
        :type parameter: ListParameter

        """
        super().__init__(parameter, parent)

        self.input = QComboBox()

        index = -1
        current_index = -1
        for opt in self._parameter.options_list:
            index += 1
            if opt == self._parameter.value:
                current_index = index
            self.input.addItem(opt)
            self.input.setItemData(index, opt, Qt.UserRole)

        self.input.setCurrentIndex(current_index)

        self.inner_input_layout.addWidget(self.input)

    def raise_invalid_type_exception(self):
        message = 'Expecting element type of %s' % (
            self._parameter.element_type.__name__)
        err = ValueError(message)
        return err

    def get_parameter(self):
        """Obtain list parameter object from the current widget state.

        :returns: A ListParameter from the current state of widget

        """
        current_index = self.input.currentIndex()
        selected_value = self.input.itemData(current_index, Qt.UserRole)
        if hasattr(selected_value, 'toPyObject'):
            selected_value = selected_value.toPyObject()

        try:
            self._parameter.value = selected_value
        except ValueError:
            err = self.raise_invalid_type_exception()
            raise err

        return self._parameter

    def set_choice(self, choice):
        """Set choice value by item's string.

        :param choice: The choice.
        :type choice: str

        :returns: True if success, else False.
        :rtype: bool
        """
        # Find index of choice
        choice_index = self._parameter.options_list.index(choice)
        if choice_index < 0:
            return False
        else:
            self.input.setCurrentIndex(choice_index)
            return True
Exemple #24
0
class frmMain(QMainWindow, Ui_frmMain):
    def __init__(self, mem, parent = 0,  flags = False):
        QMainWindow.__init__(self, None)
        self.setupUi(self)
        self.showMaximized()
        
        self.mem=mem
        self.mem.con.inactivity_timeout.connect(self.inactivity_timeout)
        
        database_update(self.mem.con, "caloriestracker", __versiondatetime__, "Qt")
        
        self.w=QWidget()       
        self.statusBar.addWidget(QLabel(self.mem.con.url_string()))

        self.mem.load_db_data() ##CARGA TODOS LOS DATOS Y LOS VINCULA       
  
        if self.mem.con.is_superuser():
            self.setWindowTitle(self.tr("Calories Tracker 2019-{0} \xa9 (Admin mode)").format(__versiondatetime__.year))#print ("Xulpymoney 2010-{0} © €".encode('unicode-escape'))
            self.setWindowIcon(self.mem.qicon_admin())
        else:
            self.setWindowTitle(self.tr("Calories Tracker 2019-{0} \xa9").format(__versiondatetime__.year))
            
        self.lblUsers=QLabel()
        self.lblUsers.setText(self.tr("Select a user")+" ")
        self.tbUsers.addWidget(self.lblUsers)
        self.cmbUsers=QComboBox()
        self.tbUsers.addWidget(self.cmbUsers)
        self.mem.data.users.qcombobox(self.cmbUsers, self.mem.user, icons=True)
        self.cmbUsers.currentIndexChanged.connect(self.on_cmbUsers_currentIndexChanged)
        
        # Adds biometric data if empty
        if self.mem.user.last_biometrics.datetime==None:
            self.on_actionBiometricsAdd_triggered()
        

    @pyqtSlot(int)
    def on_cmbUsers_currentIndexChanged(self, index):
        self.mem.user=self.mem.data.users.find_by_id(int(self.cmbUsers.itemData(self.cmbUsers.currentIndex())))
        print(self.mem.user, index)
        self.mem.settings.setValue("mem/currentuser", self.mem.user.id)
        self.on_actionBiometrics_triggered()
        qmessagebox("Changed user to {}".format(self.mem.user.name))        

    def inactivity_timeout(self):
        self.hide()
        qmessagebox(self.tr("Disconnecting due to {} minutes of inactivity.".format(self.mem.con.connectionTimeout()/60)))
        self.on_actionExit_triggered()


    @pyqtSlot()  
    def on_actionExit_triggered(self):
        self.mem.__del__()
        print ("App correctly closed")
        self.close()
        self.destroy()
        
    @pyqtSlot()
    def on_actionAbout_triggered(self):
        from caloriestracker.ui.frmAbout import frmAbout
        fr=frmAbout(self.mem)
        fr.exec_()

    @pyqtSlot()  
    def on_actionMemory_triggered(self):        
        self.mem.data.load()

    @pyqtSlot()  
    def on_actionReportIssue_triggered(self):        
            QDesktopServices.openUrl(QUrl("https://github.com/turulomio/caloriestracker/issues/new"))

    @pyqtSlot()  
    def on_actionHelp_triggered(self):
        def in_external():
            QDesktopServices.openUrl(QUrl(self.mem.url_wiki))

        try:
            user=environ['USER']
        except:
            user=None

        try: ## Remove when qwebwenginewidgets work again
            from caloriestracker.ui.frmHelp import frmHelp

            if user!=None and user=="root":
                in_external()
            else:
                w=frmHelp(self.mem, self)
                w.show()
        except:
            in_external()

    @pyqtSlot()  
    def on_actionSettings_triggered(self):
        w=frmSettings(self.mem, self)
        w.exec_()
        self.retranslateUi(self)

    @pyqtSlot()  
    def on_actionMeals_triggered(self):
        from caloriestracker.ui.wdgMeals import wdgMeals
        self.w.close()
        self.w=wdgMeals(self.mem,  self)
        self.layout.addWidget(self.w)
        self.w.show()

    @pyqtSlot()  
    def on_actionMealsMost_triggered(self):
        from caloriestracker.ui.wdgMealsMost import wdgMealsMost
        self.w.close()
        self.w=wdgMealsMost(self.mem,  self)
        self.layout.addWidget(self.w)
        self.w.show()

    @pyqtSlot()  
    def on_actionUsers_triggered(self):
        from caloriestracker.ui.wdgUsers import wdgUsers
        self.w.close()
        self.w=wdgUsers(self.mem,  self)
        self.layout.addWidget(self.w)
        self.w.show()
        
    @pyqtSlot()  
    def on_actionUserCurrent_triggered(self):
        from caloriestracker.ui.frmUsersAdd import frmUsersAdd
        w=frmUsersAdd(self.mem, self.mem.data.users.selected, self)
        w.exec_()

    @pyqtSlot()  
    def on_actionCuriosities_triggered(self):
        self.w.close()
        self.w=wdgCuriosities(self.mem,  self)
        self.layout.addWidget(self.w)
        self.w.show()

    @pyqtSlot()  
    def on_actionBiometrics_triggered(self):
        from caloriestracker.ui.wdgBiometrics import wdgBiometrics
        self.w.close()
        self.w=wdgBiometrics(self.mem,  self)
        self.layout.addWidget(self.w)
        self.w.show()

    @pyqtSlot()  
    def on_actionBiometricsAdd_triggered(self):
        from caloriestracker.ui.frmBiometricsAdd import frmBiometricsAdd
        w=frmBiometricsAdd(self.mem, None,  self)
        w.exec_()

    @pyqtSlot()  
    def on_actionCompanies_triggered(self):
        from caloriestracker.ui.wdgCompanies import wdgCompanies
        self.w.close()
        self.w=wdgCompanies(self.mem,  self)
        self.layout.addWidget(self.w)
        self.w.show()
        self.w.txt.setFocus()

    @pyqtSlot()  
    def on_actionCompaniesAdd_triggered(self):
        from caloriestracker.ui.frmCompaniesAdd import frmCompaniesAdd
        w=frmCompaniesAdd(self.mem,  None, self)
        w.exec_()
    
    @pyqtSlot()  
    def on_actionElaboratedProducts_triggered(self):
        from caloriestracker.ui.wdgProductsElaborated import wdgProductsElaborated
        self.w.close()
        self.w=wdgProductsElaborated(self.mem,  self)
        self.layout.addWidget(self.w)
        self.w.show()
        self.w.txt.setFocus()

    @pyqtSlot()  
    def on_actionElaboratedProductAdd_triggered(self):
        from caloriestracker.ui.frmProductsElaboratedAdd import frmProductsElaboratedAdd
        w=frmProductsElaboratedAdd(self.mem, None, self)
        w.exec_()
        elaborated=w.elaboratedproduct
        w=frmProductsElaboratedAdd(self.mem, elaborated, self)
        w.exec_()
        
    @pyqtSlot()  
    def on_actionProductsUpdate_triggered(self):
        p=ProductManager(self.mem)
        p.update_from_internet()
        self.actionProductsUpdate.setText(self.tr("Update products from Internet"))
        self.actionProductsUpdate.setIcon(QIcon(":/caloriestracker/cloud_download.png"))

    @pyqtSlot()  
    def on_actionProducts_triggered(self):
        self.w.close()
        from caloriestracker.ui.wdgProducts import wdgProducts
        self.w=wdgProducts(self.mem, self)
        self.layout.addWidget(self.w)
        self.w.show()
        self.w.txt.setFocus()
Exemple #25
0
class UVLightV2(COMCUPluginBase):
    def __init__(self, *args):
        super().__init__(BrickletUVLightV2, *args)

        self.uv_light = self.device

        self.cbe_uva = CallbackEmulator(self.uv_light.get_uva,
                                        None,
                                        self.cb_uva,
                                        self.increase_error_count)

        self.cbe_uvb = CallbackEmulator(self.uv_light.get_uvb,
                                        None,
                                        self.cb_uvb,
                                        self.increase_error_count)

        self.cbe_uvi = CallbackEmulator(self.uv_light.get_uvi,
                                        None,
                                        self.cb_uvi,
                                        self.increase_error_count)

        self.index_label = IndexLabel(' UVI: ? ')
        self.index_label.setText('0.0')

        self.current_uva = CurveValueWrapper()
        self.current_uvb = CurveValueWrapper()

        plots = [('UVA', Qt.red, self.current_uva, '{} mW/m²'.format),
                 ('UVB', Qt.darkGreen, self.current_uvb, '{} mW/m²'.format)]

        self.plot_widget = PlotWidget('UV [mW/m²]', plots, extra_key_widgets=[self.index_label], y_resolution=0.1)

        self.time_label = QLabel('Integration Time:')
        self.time_combo = QComboBox()
        self.time_combo.addItem("50 ms", BrickletUVLightV2.INTEGRATION_TIME_50MS)
        self.time_combo.addItem("100 ms", BrickletUVLightV2.INTEGRATION_TIME_100MS)
        self.time_combo.addItem("200 ms", BrickletUVLightV2.INTEGRATION_TIME_200MS)
        self.time_combo.addItem("400 ms", BrickletUVLightV2.INTEGRATION_TIME_400MS)
        self.time_combo.addItem("800 ms", BrickletUVLightV2.INTEGRATION_TIME_800MS)
        self.time_combo.currentIndexChanged.connect(self.new_config)

        self.saturation_label = QLabel('Sensor is saturated, choose a shorter integration time!')
        self.saturation_label.setStyleSheet('QLabel { color : red }')
        self.saturation_label.hide()

        hlayout = QHBoxLayout()
        hlayout.addWidget(self.time_label)
        hlayout.addWidget(self.time_combo)
        hlayout.addStretch()
        hlayout.addWidget(self.saturation_label)

        line = QFrame()
        line.setObjectName("line")
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)

        layout = QVBoxLayout(self)
        layout.addWidget(self.plot_widget)
        layout.addWidget(line)
        layout.addLayout(hlayout)

    def start(self):
        async_call(self.uv_light.get_configuration, None, self.get_configucation_async, self.increase_error_count)

        self.cbe_uva.set_period(100)
        self.cbe_uvb.set_period(100)
        self.cbe_uvi.set_period(100)

        self.plot_widget.stop = False

    def stop(self):
        self.cbe_uva.set_period(0)
        self.cbe_uvb.set_period(0)
        self.cbe_uvi.set_period(0)

        self.plot_widget.stop = True

    def destroy(self):
        pass

    @staticmethod
    def has_device_identifier(device_identifier):
        return device_identifier == BrickletUVLightV2.DEVICE_IDENTIFIER

    def get_configucation_async(self, integration_time):
        self.time_combo.setCurrentIndex(self.time_combo.findData(integration_time))

    def new_config(self, value):
        try:
            self.uv_light.set_configuration(self.time_combo.itemData(self.time_combo.currentIndex()))
        except:
            pass

    def cb_uva(self, uva):
        self.saturation_label.setVisible(uva < 0)

        if uva < 0: # saturated
            return

        self.current_uva.value = uva / 10.0

    def cb_uvb(self, uvb):
        self.saturation_label.setVisible(uvb < 0)

        if uvb < 0: # saturated
            return

        self.current_uvb.value = uvb / 10.0

    def cb_uvi(self, uvi):
        self.saturation_label.setVisible(uvi < 0)

        if uvi < 0: # saturated
            return

        uvi = round(uvi / 10.0, 1)

        self.index_label.setText(str(uvi))

        if uvi < 2.5:
            background = 'green'
            color = 'white'
        elif uvi < 5.5:
            background = 'yellow'
            color = 'black'
        elif uvi < 7.5:
            background = 'orange'
            color = 'black'
        elif uvi < 10.5:
            background = 'red'
            color = 'white'
        else:
            background = 'magenta'
            color = 'white'

        self.index_label.setStyleSheet('QLabel {{ background : {0}; color : {1} }}'.format(background, color))
class AmbientLightV3(COMCUPluginBase):
    def __init__(self, *args):
        super().__init__(BrickletAmbientLightV3, *args)

        self.al = self.device

        self.cbe_illuminance = CallbackEmulator(self.al.get_illuminance,
                                                None,
                                                self.cb_illuminance,
                                                self.increase_error_count)

        self.alf = AmbientLightFrame()
        self.out_of_range_label = QLabel('Illuminance is out-of-range')
        self.saturated_label = QLabel('Sensor is saturated')

        self.out_of_range_label.hide()
        self.out_of_range_label.setStyleSheet('QLabel { color: red }')
        self.saturated_label.hide()
        self.saturated_label.setStyleSheet('QLabel { color: magenta }')

        self.current_illuminance = CurveValueWrapper() # float, lx

        plots = [('Illuminance', Qt.red, self.current_illuminance, '{:.2f} lx (Lux)'.format)]
        self.plot_widget = PlotWidget('Illuminance [lx]', plots,
                                      extra_key_widgets=[self.out_of_range_label, self.saturated_label, self.alf],
                                      y_resolution=0.001)

        self.range_label = QLabel('Illuminance Range:')
        self.range_combo = QComboBox()

        self.range_combo.addItem("Unlimited", BrickletAmbientLightV3.ILLUMINANCE_RANGE_UNLIMITED)
        self.range_combo.addItem("0 - 64000 lx", BrickletAmbientLightV3.ILLUMINANCE_RANGE_64000LUX)
        self.range_combo.addItem("0 - 32000 lx", BrickletAmbientLightV3.ILLUMINANCE_RANGE_32000LUX)
        self.range_combo.addItem("0 - 16000 lx", BrickletAmbientLightV3.ILLUMINANCE_RANGE_16000LUX)
        self.range_combo.addItem("0 - 8000 lx", BrickletAmbientLightV3.ILLUMINANCE_RANGE_8000LUX)
        self.range_combo.addItem("0 - 1300 lx", BrickletAmbientLightV3.ILLUMINANCE_RANGE_1300LUX)
        self.range_combo.addItem("0 - 600 lx", BrickletAmbientLightV3.ILLUMINANCE_RANGE_600LUX)
        self.range_combo.currentIndexChanged.connect(self.new_config)

        self.time_label = QLabel('Integration Time:')
        self.time_combo = QComboBox()
        self.time_combo.addItem("50 ms", BrickletAmbientLightV3.INTEGRATION_TIME_50MS)
        self.time_combo.addItem("100 ms", BrickletAmbientLightV3.INTEGRATION_TIME_100MS)
        self.time_combo.addItem("150 ms", BrickletAmbientLightV3.INTEGRATION_TIME_150MS)
        self.time_combo.addItem("200 ms", BrickletAmbientLightV3.INTEGRATION_TIME_200MS)
        self.time_combo.addItem("250 ms", BrickletAmbientLightV3.INTEGRATION_TIME_250MS)
        self.time_combo.addItem("300 ms", BrickletAmbientLightV3.INTEGRATION_TIME_300MS)
        self.time_combo.addItem("350 ms", BrickletAmbientLightV3.INTEGRATION_TIME_350MS)
        self.time_combo.addItem("400 ms", BrickletAmbientLightV3.INTEGRATION_TIME_400MS)
        self.time_combo.currentIndexChanged.connect(self.new_config)

        hlayout = QHBoxLayout()
        hlayout.addWidget(self.range_label)
        hlayout.addWidget(self.range_combo)
        hlayout.addStretch()
        hlayout.addWidget(self.time_label)
        hlayout.addWidget(self.time_combo)

        line = QFrame()
        line.setObjectName("line")
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)

        layout = QVBoxLayout(self)
        layout.addWidget(self.plot_widget)
        layout.addWidget(line)
        layout.addLayout(hlayout)

    def start(self):
        async_call(self.al.get_configuration, None, self.get_configucation_async, self.increase_error_count)

        self.cbe_illuminance.set_period(100)

        self.plot_widget.stop = False

    def stop(self):
        self.cbe_illuminance.set_period(0)

        self.plot_widget.stop = True

    def destroy(self):
        pass

    @staticmethod
    def has_device_identifier(device_identifier):
        return device_identifier == BrickletAmbientLightV3.DEVICE_IDENTIFIER

    def get_configucation_async(self, conf):
        self.range_combo.setCurrentIndex(self.range_combo.findData(conf.illuminance_range))
        self.time_combo.setCurrentIndex(self.time_combo.findData(conf.integration_time))

    def new_config(self, _value):
        try:
            self.al.set_configuration(self.range_combo.itemData(self.range_combo.currentIndex()),
                                      self.time_combo.itemData(self.time_combo.currentIndex()))
        except ip_connection.Error:
            pass

    def cb_illuminance(self, illuminance):
        self.current_illuminance.value = illuminance / 100.0

        max_illuminance = 12000000 # Approximation for unlimited range
        current_range = self.range_combo.itemData(self.range_combo.currentIndex())

        if current_range == BrickletAmbientLightV3.ILLUMINANCE_RANGE_64000LUX:
            max_illuminance = 6400001
        elif current_range == BrickletAmbientLightV3.ILLUMINANCE_RANGE_32000LUX:
            max_illuminance = 3200001
        elif current_range == BrickletAmbientLightV3.ILLUMINANCE_RANGE_16000LUX:
            max_illuminance = 1600001
        elif current_range == BrickletAmbientLightV3.ILLUMINANCE_RANGE_8000LUX:
            max_illuminance = 800001
        elif current_range == BrickletAmbientLightV3.ILLUMINANCE_RANGE_1300LUX:
            max_illuminance = 130001
        elif current_range == BrickletAmbientLightV3.ILLUMINANCE_RANGE_600LUX:
            max_illuminance = 60001

        if illuminance == 0:
            self.plot_widget.get_key_item(0).setStyleSheet('QLabel { color: magenta }')
            self.out_of_range_label.hide()
            self.saturated_label.show()
        elif illuminance >= max_illuminance:
            self.plot_widget.get_key_item(0).setStyleSheet('QLabel { color: red }')
            self.out_of_range_label.show()
            self.saturated_label.hide()
        else:
            self.plot_widget.get_key_item(0).setStyleSheet('')
            self.out_of_range_label.hide()
            self.saturated_label.hide()

        value = min(max(illuminance * 255 // max_illuminance, 0), 255)
        self.alf.set_color(value, value, value)
Exemple #27
0
class SearchWidget(QWidget, WidgetMixin):
    def __init__(self, **kwargs):
        super(SearchWidget, self).__init__(**kwargs)

        layout = QGridLayout()
        self.setLayout(layout)

        self.exprEdit = QLineEdit()
        self.exprEdit.returnPressed.connect(self.returnPressed)
        self.setFocusProxy(self.exprEdit)

        self.optionsButton = SearchOptionsButton()

        self.pluginChoice = QComboBox()
        plugins = sorted(file_search.enabledPlugins(), key=lambda p: p.name())
        for plugin in plugins:
            self.pluginChoice.addItem(plugin.name(), plugin.id)

        self.results = LocationList()
        self.results.setColumns(['path', 'line', 'snippet'])

        self.searcher = None

        layout.addWidget(self.exprEdit, 0, 0)
        layout.addWidget(self.optionsButton, 0, 1)
        layout.addWidget(self.pluginChoice, 0, 2)
        layout.addWidget(self.results, 1, 0, 1, -1)

        self.addCategory('file_search_widget')

    def setPlugin(self, id):
        index = self.pluginChoice.findData(id)
        if index >= 0:
            self.pluginChoice.setCurrentIndex(index)

    def setText(self, text):
        self.exprEdit.setText(text)

    def selectedPlugin(self):
        return self.pluginChoice.itemData(self.pluginChoice.currentIndex())

    def regexp(self):
        re = QRegExp(self.exprEdit.text())
        re.setCaseSensitivity(csToQtEnum(self.optionsButton.caseSensitive()))
        re.setPatternSyntax(self.optionsButton.reFormat())
        return re

    def shouldFindRoot(self):
        return self.optionsButton.shouldFindRoot()

    def makeArgs(self, plugin):
        ed = buffers.currentBuffer()

        if self.shouldFindRoot():
            path = plugin.searchRootPath(ed.path)
        else:
            path = os.path.dirname(ed.path)
        pattern = self.exprEdit.text()
        ci = self.optionsButton.caseSensitive()
        return (path, pattern, ci)

    @Slot()
    def doSearch(self):
        self.results.clear()
        plugin_type = file_search.getPlugin(self.selectedPlugin())
        self.searcher = plugin_type()
        file_search.setupLocationList(self.searcher, self.results)
        args = self.makeArgs(self.searcher)
        self.searcher.search(*args)

    returnPressed = Signal()
Exemple #28
0
class SettingsDialog(QDialog):
    worker = None
    config = None
    configfile = None

    saved = QtCore.pyqtSignal()

    def __init__(self, parent, worker, config, configfile):
        QDialog.__init__(self, parent)

        self.worker = worker
        self.config = config
        self.configfile = configfile
        self.setStyleSheet("QGroupBox { font-weight: bold; } ")
        self.setWindowTitle('Settings')
        layout = QGridLayout()

        # Categories
        self.category_list = QListWidget()
        category_media = QListWidgetItem(getIcon('media-playback-start'), 'Media', self.category_list)
        category_sync = QListWidgetItem(getIcon('view-refresh'), 'Sync', self.category_list)
        category_ui = QListWidgetItem(getIcon('window-new'), 'User Interface', self.category_list)
        category_theme = QListWidgetItem(getIcon('applications-graphics'), 'Theme', self.category_list)
        self.category_list.setSelectionMode(QAbstractItemView.SingleSelection)
        self.category_list.setCurrentRow(0)
        self.category_list.setMaximumWidth(self.category_list.sizeHintForColumn(0) + 15)
        self.category_list.setFocus()
        self.category_list.currentItemChanged.connect(self.s_switch_page)

        # Media tab
        page_media = QWidget()
        page_media_layout = QVBoxLayout()
        page_media_layout.setAlignment(QtCore.Qt.AlignTop)

        # Group: Media settings
        g_media = QGroupBox('Media settings')
        g_media.setFlat(True)
        g_media_layout = QFormLayout()
        self.tracker_enabled = QCheckBox()
        self.tracker_enabled.toggled.connect(self.tracker_type_change)

        self.tracker_type = QComboBox()
        for (n, label) in utils.available_trackers:
            self.tracker_type.addItem(label, n)
        self.tracker_type.currentIndexChanged.connect(self.tracker_type_change)

        self.tracker_interval = QSpinBox()
        self.tracker_interval.setRange(5, 1000)
        self.tracker_interval.setMaximumWidth(60)
        self.tracker_process = QLineEdit()
        self.tracker_update_wait = QSpinBox()
        self.tracker_update_wait.setRange(0, 1000)
        self.tracker_update_wait.setMaximumWidth(60)
        self.tracker_update_close = QCheckBox()
        self.tracker_update_prompt = QCheckBox()
        self.tracker_not_found_prompt = QCheckBox()

        g_media_layout.addRow('Enable tracker', self.tracker_enabled)
        g_media_layout.addRow('Tracker type', self.tracker_type)
        g_media_layout.addRow('Tracker interval (seconds)', self.tracker_interval)
        g_media_layout.addRow('Process name (regex)', self.tracker_process)
        g_media_layout.addRow('Wait before updating (seconds)', self.tracker_update_wait)
        g_media_layout.addRow('Wait until the player is closed', self.tracker_update_close)
        g_media_layout.addRow('Ask before updating', self.tracker_update_prompt)
        g_media_layout.addRow('Ask to add new shows', self.tracker_not_found_prompt)

        g_media.setLayout(g_media_layout)

        # Group: Plex settings
        g_plex = QGroupBox('Plex Media Server')
        g_plex.setFlat(True)
        self.plex_host = QLineEdit()
        self.plex_port = QLineEdit()
        self.plex_user = QLineEdit()
        self.plex_passw = QLineEdit()
        self.plex_passw.setEchoMode(QLineEdit.Password)
        self.plex_obey_wait = QCheckBox()

        g_plex_layout = QGridLayout()
        g_plex_layout.addWidget(QLabel('Host and Port'),                   0, 0, 1, 1)
        g_plex_layout.addWidget(self.plex_host,                            0, 1, 1, 1)
        g_plex_layout.addWidget(self.plex_port,                            0, 2, 1, 2)
        g_plex_layout.addWidget(QLabel('Use "wait before updating" time'), 1, 0, 1, 1)
        g_plex_layout.addWidget(self.plex_obey_wait,                       1, 2, 1, 1)
        g_plex_layout.addWidget(QLabel('myPlex login (claimed server)'),   2, 0, 1, 1)
        g_plex_layout.addWidget(self.plex_user,                            2, 1, 1, 1)
        g_plex_layout.addWidget(self.plex_passw,                           2, 2, 1, 2)

        g_plex.setLayout(g_plex_layout)

        # Group: Library
        g_playnext = QGroupBox('Library')
        g_playnext.setFlat(True)
        self.player = QLineEdit()
        self.player_browse = QPushButton('Browse...')
        self.player_browse.clicked.connect(self.s_player_browse)
        lbl_searchdirs = QLabel('Media directories')
        lbl_searchdirs.setAlignment(QtCore.Qt.AlignTop)
        self.searchdirs = QListWidget()
        self.searchdirs_add = QPushButton('Add...')
        self.searchdirs_add.clicked.connect(self.s_searchdirs_add)
        self.searchdirs_remove = QPushButton('Remove')
        self.searchdirs_remove.clicked.connect(self.s_searchdirs_remove)
        self.searchdirs_buttons = QVBoxLayout()
        self.searchdirs_buttons.setAlignment(QtCore.Qt.AlignTop)
        self.searchdirs_buttons.addWidget(self.searchdirs_add)
        self.searchdirs_buttons.addWidget(self.searchdirs_remove)
        self.searchdirs_buttons.addWidget(QSplitter())
        self.library_autoscan = QCheckBox()
        self.scan_whole_list = QCheckBox()
        self.library_full_path = QCheckBox()


        g_playnext_layout = QGridLayout()
        g_playnext_layout.addWidget(QLabel('Player'),                    0, 0, 1, 1)
        g_playnext_layout.addWidget(self.player,                         0, 1, 1, 1)
        g_playnext_layout.addWidget(self.player_browse,                  0, 2, 1, 1)
        g_playnext_layout.addWidget(lbl_searchdirs,                      1, 0, 1, 1)
        g_playnext_layout.addWidget(self.searchdirs,                     1, 1, 1, 1)
        g_playnext_layout.addLayout(self.searchdirs_buttons,             1, 2, 1, 1)
        g_playnext_layout.addWidget(QLabel('Rescan Library at startup'), 2, 0, 1, 2)
        g_playnext_layout.addWidget(self.library_autoscan,               2, 2, 1, 1)
        g_playnext_layout.addWidget(QLabel('Scan through whole list'),   3, 0, 1, 2)
        g_playnext_layout.addWidget(self.scan_whole_list,                3, 2, 1, 1)
        g_playnext_layout.addWidget(QLabel('Take subdirectory name into account'), 4, 0, 1, 2)
        g_playnext_layout.addWidget(self.library_full_path,              4, 2, 1, 1)

        g_playnext.setLayout(g_playnext_layout)

        # Media form
        page_media_layout.addWidget(g_media)
        page_media_layout.addWidget(g_plex)
        page_media_layout.addWidget(g_playnext)
        page_media.setLayout(page_media_layout)

        # Sync tab
        page_sync = QWidget()
        page_sync_layout = QVBoxLayout()
        page_sync_layout.setAlignment(QtCore.Qt.AlignTop)

        # Group: Autoretrieve
        g_autoretrieve = QGroupBox('Autoretrieve')
        g_autoretrieve.setFlat(True)
        self.autoretrieve_off = QRadioButton('Disabled')
        self.autoretrieve_always = QRadioButton('Always at start')
        self.autoretrieve_days = QRadioButton('After n days')
        self.autoretrieve_days.toggled.connect(self.s_autoretrieve_days)
        self.autoretrieve_days_n = QSpinBox()
        self.autoretrieve_days_n.setRange(1, 100)
        g_autoretrieve_layout = QGridLayout()
        g_autoretrieve_layout.setColumnStretch(0, 1)
        g_autoretrieve_layout.addWidget(self.autoretrieve_off,    0, 0, 1, 1)
        g_autoretrieve_layout.addWidget(self.autoretrieve_always, 1, 0, 1, 1)
        g_autoretrieve_layout.addWidget(self.autoretrieve_days,   2, 0, 1, 1)
        g_autoretrieve_layout.addWidget(self.autoretrieve_days_n, 2, 1, 1, 1)
        g_autoretrieve.setLayout(g_autoretrieve_layout)

        # Group: Autosend
        g_autosend = QGroupBox('Autosend')
        g_autosend.setFlat(True)
        self.autosend_off = QRadioButton('Disabled')
        self.autosend_always = QRadioButton('Immediately after every change')
        self.autosend_minutes = QRadioButton('After n minutes')
        self.autosend_minutes.toggled.connect(self.s_autosend_minutes)
        self.autosend_minutes_n = QSpinBox()
        self.autosend_minutes_n.setRange(1, 1000)
        self.autosend_size = QRadioButton('After the queue reaches n items')
        self.autosend_size.toggled.connect(self.s_autosend_size)
        self.autosend_size_n = QSpinBox()
        self.autosend_size_n.setRange(2, 20)
        self.autosend_at_exit = QCheckBox('At exit')
        g_autosend_layout = QGridLayout()
        g_autosend_layout.setColumnStretch(0, 1)
        g_autosend_layout.addWidget(self.autosend_off,      0, 0, 1, 1)
        g_autosend_layout.addWidget(self.autosend_always,   1, 0, 1, 1)
        g_autosend_layout.addWidget(self.autosend_minutes,    2, 0, 1, 1)
        g_autosend_layout.addWidget(self.autosend_minutes_n,  2, 1, 1, 1)
        g_autosend_layout.addWidget(self.autosend_size,     3, 0, 1, 1)
        g_autosend_layout.addWidget(self.autosend_size_n,   3, 1, 1, 1)
        g_autosend_layout.addWidget(self.autosend_at_exit,  4, 0, 1, 1)
        g_autosend.setLayout(g_autosend_layout)

        # Group: Extra
        g_extra = QGroupBox('Additional options')
        g_extra.setFlat(True)
        self.auto_status_change = QCheckBox('Change status automatically')
        self.auto_status_change.toggled.connect(self.s_auto_status_change)
        self.auto_status_change_if_scored = QCheckBox('Change status automatically only if scored')
        self.auto_date_change = QCheckBox('Change start and finish dates automatically')
        g_extra_layout = QVBoxLayout()
        g_extra_layout.addWidget(self.auto_status_change)
        g_extra_layout.addWidget(self.auto_status_change_if_scored)
        g_extra_layout.addWidget(self.auto_date_change)
        g_extra.setLayout(g_extra_layout)

        # Sync layout
        page_sync_layout.addWidget(g_autoretrieve)
        page_sync_layout.addWidget(g_autosend)
        page_sync_layout.addWidget(g_extra)
        page_sync.setLayout(page_sync_layout)

        # UI tab
        page_ui = QWidget()
        page_ui_layout = QFormLayout()
        page_ui_layout.setAlignment(QtCore.Qt.AlignTop)

        # Group: Icon
        g_icon = QGroupBox('Notification Icon')
        g_icon.setFlat(True)
        self.tray_icon = QCheckBox('Show tray icon')
        self.tray_icon.toggled.connect(self.s_tray_icon)
        self.close_to_tray = QCheckBox('Close to tray')
        self.start_in_tray = QCheckBox('Start minimized to tray')
        self.tray_api_icon = QCheckBox('Use API icon as tray icon')
        self.notifications = QCheckBox('Show notification when tracker detects new media')
        g_icon_layout = QVBoxLayout()
        g_icon_layout.addWidget(self.tray_icon)
        g_icon_layout.addWidget(self.close_to_tray)
        g_icon_layout.addWidget(self.start_in_tray)
        g_icon_layout.addWidget(self.tray_api_icon)
        g_icon_layout.addWidget(self.notifications)
        g_icon.setLayout(g_icon_layout)

        # Group: Window
        g_window = QGroupBox('Window')
        g_window.setFlat(True)
        self.remember_geometry = QCheckBox('Remember window size and position')
        self.remember_columns = QCheckBox('Remember column layouts and widths')
        self.columns_per_api = QCheckBox('Use different visible columns per API')
        g_window_layout = QVBoxLayout()
        g_window_layout.addWidget(self.remember_geometry)
        g_window_layout.addWidget(self.remember_columns)
        g_window_layout.addWidget(self.columns_per_api)
        g_window.setLayout(g_window_layout)

        # Group: Lists
        g_lists = QGroupBox('Lists')
        g_lists.setFlat(True)
        self.filter_bar_position = QComboBox()
        filter_bar_positions = [(FilterBar.PositionHidden,     'Hidden'),
                                (FilterBar.PositionAboveLists, 'Above lists'),
                                (FilterBar.PositionBelowLists, 'Below lists')]
        for (n, label) in filter_bar_positions:
            self.filter_bar_position.addItem(label, n)
        self.inline_edit = QCheckBox('Enable in-line editing')
        g_lists_layout = QFormLayout()
        g_lists_layout.addRow('Filter bar position:', self.filter_bar_position)
        g_lists_layout.addRow(self.inline_edit)
        g_lists.setLayout(g_lists_layout)

        # UI layout
        page_ui_layout.addWidget(g_icon)
        page_ui_layout.addWidget(g_window)
        page_ui_layout.addWidget(g_lists)
        page_ui.setLayout(page_ui_layout)

        # Theming tab
        page_theme = QWidget()
        page_theme_layout = QFormLayout()
        page_theme_layout.setAlignment(QtCore.Qt.AlignTop)

        # Group: Episode Bar
        g_ep_bar = QGroupBox('Episode Bar')
        g_ep_bar.setFlat(True)
        self.ep_bar_style = QComboBox()
        ep_bar_styles = [(ShowsTableDelegate.BarStyleBasic,  'Basic'),
                         (ShowsTableDelegate.BarStyle04,     'Trackma'),
                         (ShowsTableDelegate.BarStyleHybrid, 'Hybrid')]
        for (n, label) in ep_bar_styles:
            self.ep_bar_style.addItem(label, n)
        self.ep_bar_style.currentIndexChanged.connect(self.s_ep_bar_style)
        self.ep_bar_text = QCheckBox('Show text label')
        g_ep_bar_layout = QFormLayout()
        g_ep_bar_layout.addRow('Style:', self.ep_bar_style)
        g_ep_bar_layout.addRow(self.ep_bar_text)
        g_ep_bar.setLayout(g_ep_bar_layout)

        # Group: Colour scheme
        g_scheme = QGroupBox('Color Scheme')
        g_scheme.setFlat(True)
        col_tabs = [('rows',     '&Row highlights'),
                    ('progress', '&Progress widget')]
        self.colors = {}
        self.colors['rows'] = [('is_playing',  'Playing'),
                               ('is_queued',   'Queued'),
                               ('new_episode', 'New Episode'),
                               ('is_airing',   'Airing'),
                               ('not_aired',   'Unaired')]
        self.colors['progress'] = [('progress_bg',       'Background'),
                                   ('progress_fg',       'Watched bar'),
                                   ('progress_sub_bg',   'Aired episodes'),
                                   ('progress_sub_fg',   'Stored episodes'),
                                   ('progress_complete', 'Complete')]
        self.color_buttons = []
        self.syscolor_buttons = []
        g_scheme_layout = QGridLayout()
        tw_scheme = QTabWidget()
        for (key, tab_title) in col_tabs:
            page = QFrame()
            page_layout = QGridLayout()
            col = 0
            # Generate widgets from the keys and values
            for (key, label) in self.colors[key]:
                self.color_buttons.append(QPushButton())
                # self.color_buttons[-1].setStyleSheet('background-color: ' + getColor(self.config['colors'][key]).name())
                self.color_buttons[-1].setFocusPolicy(QtCore.Qt.NoFocus)
                self.color_buttons[-1].clicked.connect(self.s_color_picker(key, False))
                self.syscolor_buttons.append(QPushButton('System Colors'))
                self.syscolor_buttons[-1].clicked.connect(self.s_color_picker(key, True))
                page_layout.addWidget(QLabel(label),             col, 0, 1, 1)
                page_layout.addWidget(self.color_buttons[-1],    col, 1, 1, 1)
                page_layout.addWidget(self.syscolor_buttons[-1], col, 2, 1, 1)
                col += 1
            page.setLayout(page_layout)
            tw_scheme.addTab(page, tab_title)
        g_scheme_layout.addWidget(tw_scheme)
        g_scheme.setLayout(g_scheme_layout)

        # UI layout
        page_theme_layout.addWidget(g_ep_bar)
        page_theme_layout.addWidget(g_scheme)
        page_theme.setLayout(page_theme_layout)

        # Content
        self.contents = QStackedWidget()
        self.contents.addWidget(page_media)
        self.contents.addWidget(page_sync)
        self.contents.addWidget(page_ui)
        self.contents.addWidget(page_theme)
        if pyqt_version is not 5:
            self.contents.layout().setMargin(0)

        # Bottom buttons
        bottombox = QDialogButtonBox(
            QDialogButtonBox.Ok
            | QDialogButtonBox.Apply
            | QDialogButtonBox.Cancel
        )
        bottombox.accepted.connect(self.s_save)
        bottombox.button(QDialogButtonBox.Apply).clicked.connect(self._save)
        bottombox.rejected.connect(self.reject)

        # Main layout finish
        layout.addWidget(self.category_list,  0, 0, 1, 1)
        layout.addWidget(self.contents,       0, 1, 1, 1)
        layout.addWidget(bottombox,           1, 0, 1, 2)
        layout.setColumnStretch(1, 1)

        self._load()
        self.update_colors()

        self.setLayout(layout)

    def _add_dir(self, path):
        self.searchdirs.addItem(path)

    def _load(self):
        engine = self.worker.engine
        tracker_type = self.tracker_type.findData(engine.get_config('tracker_type'))
        autoretrieve = engine.get_config('autoretrieve')
        autosend = engine.get_config('autosend')

        self.tracker_enabled.setChecked(engine.get_config('tracker_enabled'))
        self.tracker_type.setCurrentIndex(max(0, tracker_type))
        self.tracker_interval.setValue(engine.get_config('tracker_interval'))
        self.tracker_process.setText(engine.get_config('tracker_process'))
        self.tracker_update_wait.setValue(engine.get_config('tracker_update_wait_s'))
        self.tracker_update_close.setChecked(engine.get_config('tracker_update_close'))
        self.tracker_update_prompt.setChecked(engine.get_config('tracker_update_prompt'))
        self.tracker_not_found_prompt.setChecked(engine.get_config('tracker_not_found_prompt'))

        self.player.setText(engine.get_config('player'))
        self.library_autoscan.setChecked(engine.get_config('library_autoscan'))
        self.scan_whole_list.setChecked(engine.get_config('scan_whole_list'))
        self.library_full_path.setChecked(engine.get_config('library_full_path'))
        self.plex_host.setText(engine.get_config('plex_host'))
        self.plex_port.setText(engine.get_config('plex_port'))
        self.plex_obey_wait.setChecked(engine.get_config('plex_obey_update_wait_s'))
        self.plex_user.setText(engine.get_config('plex_user'))
        self.plex_passw.setText(engine.get_config('plex_passwd'))

        for path in engine.get_config('searchdir'):
            self._add_dir(path)

        if autoretrieve == 'always':
            self.autoretrieve_always.setChecked(True)
        elif autoretrieve == 'days':
            self.autoretrieve_days.setChecked(True)
        else:
            self.autoretrieve_off.setChecked(True)

        self.autoretrieve_days_n.setValue(engine.get_config('autoretrieve_days'))

        if autosend == 'always':
            self.autosend_always.setChecked(True)
        elif autosend in ('minutes', 'hours'):
            self.autosend_minutes.setChecked(True)
        elif autosend == 'size':
            self.autosend_size.setChecked(True)
        else:
            self.autosend_off.setChecked(True)

        self.autosend_minutes_n.setValue(engine.get_config('autosend_minutes'))
        self.autosend_size_n.setValue(engine.get_config('autosend_size'))

        self.autosend_at_exit.setChecked(engine.get_config('autosend_at_exit'))
        self.auto_status_change.setChecked(engine.get_config('auto_status_change'))
        self.auto_status_change_if_scored.setChecked(engine.get_config('auto_status_change_if_scored'))
        self.auto_date_change.setChecked(engine.get_config('auto_date_change'))

        self.tray_icon.setChecked(self.config['show_tray'])
        self.close_to_tray.setChecked(self.config['close_to_tray'])
        self.start_in_tray.setChecked(self.config['start_in_tray'])
        self.tray_api_icon.setChecked(self.config['tray_api_icon'])
        self.notifications.setChecked(self.config['notifications'])
        self.remember_geometry.setChecked(self.config['remember_geometry'])
        self.remember_columns.setChecked(self.config['remember_columns'])
        self.columns_per_api.setChecked(self.config['columns_per_api'])
        self.filter_bar_position.setCurrentIndex(self.filter_bar_position.findData(self.config['filter_bar_position']))
        self.inline_edit.setChecked(self.config['inline_edit'])

        self.ep_bar_style.setCurrentIndex(self.ep_bar_style.findData(self.config['episodebar_style']))
        self.ep_bar_text.setChecked(self.config['episodebar_text'])

        self.autoretrieve_days_n.setEnabled(self.autoretrieve_days.isChecked())
        self.autosend_minutes_n.setEnabled(self.autosend_minutes.isChecked())
        self.autosend_size_n.setEnabled(self.autosend_size.isChecked())
        self.close_to_tray.setEnabled(self.tray_icon.isChecked())
        self.start_in_tray.setEnabled(self.tray_icon.isChecked())
        self.notifications.setEnabled(self.tray_icon.isChecked())

        self.color_values = self.config['colors'].copy()

        self.tracker_type_change(None)

    def _save(self):
        engine = self.worker.engine

        engine.set_config('tracker_enabled',       self.tracker_enabled.isChecked())
        engine.set_config('tracker_type',          self.tracker_type.itemData(self.tracker_type.currentIndex()))
        engine.set_config('tracker_interval',      self.tracker_interval.value())
        engine.set_config('tracker_process',       str(self.tracker_process.text()))
        engine.set_config('tracker_update_wait_s', self.tracker_update_wait.value())
        engine.set_config('tracker_update_close',  self.tracker_update_close.isChecked())
        engine.set_config('tracker_update_prompt', self.tracker_update_prompt.isChecked())
        engine.set_config('tracker_not_found_prompt', self.tracker_not_found_prompt.isChecked())

        engine.set_config('player',            self.player.text())
        engine.set_config('library_autoscan',  self.library_autoscan.isChecked())
        engine.set_config('scan_whole_list', self.scan_whole_list.isChecked())
        engine.set_config('library_full_path', self.library_full_path.isChecked())
        engine.set_config('plex_host',         self.plex_host.text())
        engine.set_config('plex_port',         self.plex_port.text())
        engine.set_config('plex_obey_update_wait_s', self.plex_obey_wait.isChecked())
        engine.set_config('plex_user',         self.plex_user.text())
        engine.set_config('plex_passwd',       self.plex_passw.text())

        engine.set_config('searchdir',         [self.searchdirs.item(i).text() for i in range(self.searchdirs.count())])

        if self.autoretrieve_always.isChecked():
            engine.set_config('autoretrieve', 'always')
        elif self.autoretrieve_days.isChecked():
            engine.set_config('autoretrieve', 'days')
        else:
            engine.set_config('autoretrieve', 'off')

        engine.set_config('autoretrieve_days',   self.autoretrieve_days_n.value())

        if self.autosend_always.isChecked():
            engine.set_config('autosend', 'always')
        elif self.autosend_minutes.isChecked():
            engine.set_config('autosend', 'minutes')
        elif self.autosend_size.isChecked():
            engine.set_config('autosend', 'size')
        else:
            engine.set_config('autosend', 'off')

        engine.set_config('autosend_minutes', self.autosend_minutes_n.value())
        engine.set_config('autosend_size',  self.autosend_size_n.value())

        engine.set_config('autosend_at_exit',   self.autosend_at_exit.isChecked())
        engine.set_config('auto_status_change', self.auto_status_change.isChecked())
        engine.set_config('auto_status_change_if_scored', self.auto_status_change_if_scored.isChecked())
        engine.set_config('auto_date_change',   self.auto_date_change.isChecked())

        engine.save_config()

        self.config['show_tray'] = self.tray_icon.isChecked()
        self.config['close_to_tray'] = self.close_to_tray.isChecked()
        self.config['start_in_tray'] = self.start_in_tray.isChecked()
        self.config['tray_api_icon'] = self.tray_api_icon.isChecked()
        self.config['notifications'] = self.notifications.isChecked()
        self.config['remember_geometry'] = self.remember_geometry.isChecked()
        self.config['remember_columns'] = self.remember_columns.isChecked()
        self.config['columns_per_api'] = self.columns_per_api.isChecked()
        self.config['filter_bar_position'] = self.filter_bar_position.itemData(self.filter_bar_position.currentIndex())
        self.config['inline_edit'] = self.inline_edit.isChecked()

        self.config['episodebar_style'] = self.ep_bar_style.itemData(self.ep_bar_style.currentIndex())
        self.config['episodebar_text'] = self.ep_bar_text.isChecked()

        self.config['colors'] = self.color_values

        utils.save_config(self.config, self.configfile)

        self.saved.emit()

    def s_save(self):
        self._save()
        self.accept()

    def tracker_type_change(self, checked):
        if self.tracker_enabled.isChecked():
            self.tracker_interval.setEnabled(True)
            self.tracker_update_wait.setEnabled(True)
            self.tracker_type.setEnabled(True)
            if self.tracker_type.itemData(self.tracker_type.currentIndex()) == 'plex':
                self.plex_host.setEnabled(True)
                self.plex_port.setEnabled(True)
                self.plex_obey_wait.setEnabled(True)
                self.plex_user.setEnabled(True)
                self.plex_passw.setEnabled(True)
                self.tracker_process.setEnabled(False)
            else:
                self.tracker_process.setEnabled(True)
                self.plex_host.setEnabled(False)
                self.plex_port.setEnabled(False)
                self.plex_user.setEnabled(False)
                self.plex_passw.setEnabled(False)
                self.plex_obey_wait.setEnabled(False)
        else:
            self.tracker_type.setEnabled(False)
            self.plex_host.setEnabled(False)
            self.plex_port.setEnabled(False)
            self.plex_user.setEnabled(False)
            self.plex_passw.setEnabled(False)
            self.plex_obey_wait.setEnabled(False)
            self.tracker_process.setEnabled(False)
            self.tracker_interval.setEnabled(False)
            self.tracker_update_wait.setEnabled(False)

    def s_autoretrieve_days(self, checked):
        self.autoretrieve_days_n.setEnabled(checked)

    def s_autosend_minutes(self, checked):
        self.autosend_minutes_n.setEnabled(checked)

    def s_autosend_size(self, checked):
        self.autosend_size_n.setEnabled(checked)

    def s_tray_icon(self, checked):
        self.close_to_tray.setEnabled(checked)
        self.start_in_tray.setEnabled(checked)
        self.tray_api_icon.setEnabled(checked)
        self.notifications.setEnabled(checked)

    def s_ep_bar_style(self, index):
        if self.ep_bar_style.itemData(index) == ShowsTableDelegate.BarStyle04:
            self.ep_bar_text.setEnabled(False)
        else:
            self.ep_bar_text.setEnabled(True)

    def s_auto_status_change(self, checked):
        self.auto_status_change_if_scored.setEnabled(checked)

    def s_player_browse(self):
        if pyqt_version is 5:
            self.player.setText(QFileDialog.getOpenFileName(caption='Choose player executable')[0])
        else:
            self.player.setText(QFileDialog.getOpenFileName(caption='Choose player executable'))

    def s_searchdirs_add(self):
        self._add_dir(QFileDialog.getExistingDirectory(caption='Choose media directory'))

    def s_searchdirs_remove(self):
        row = self.searchdirs.currentRow()
        if row != -1:
            self.searchdirs.takeItem(row)

    def s_switch_page(self, new, old):
        if not new:
            new = old

        self.contents.setCurrentIndex(self.category_list.row(new))

    def s_color_picker(self, key, system):
        return lambda: self.color_picker(key, system)

    def color_picker(self, key, system):
        if system is True:
            current = self.color_values[key]
            result = ThemedColorPicker.do()
            if result is not None and result is not current:
                self.color_values[key] = result
                self.update_colors()
        else:
            current = getColor(self.color_values[key])
            result = QColorDialog.getColor(current)
            if result.isValid() and result is not current:
                self.color_values[key] = str(result.name())
                self.update_colors()

    def update_colors(self):
        for ((key, label), color) in zip(self.colors['rows']+self.colors['progress'], self.color_buttons):
            color.setStyleSheet('background-color: ' + getColor(self.color_values[key]).name())
Exemple #29
0
class Thermocouple(PluginBase):
    qtcb_error_state = pyqtSignal(bool, bool)

    def __init__(self, *args):
        super().__init__(BrickletThermocouple, *args)

        self.thermo = self.device

        self.qtcb_error_state.connect(self.cb_error_state)
        self.thermo.register_callback(self.thermo.CALLBACK_ERROR_STATE,
                                      self.qtcb_error_state.emit)

        self.cbe_temperature = CallbackEmulator(self.thermo.get_temperature,
                                                None,
                                                self.cb_temperature,
                                                self.increase_error_count)

        self.current_temperature = CurveValueWrapper() # float, °C

        self.error_label = QLabel('Current Errors: None')
        self.error_label.setAlignment(Qt.AlignVCenter | Qt.AlignRight)

        plots = [('Temperature', Qt.red, self.current_temperature, '{:.2f} °C'.format)]
        self.plot_widget = PlotWidget('Temperature [°C]', plots, extra_key_widgets=[self.error_label], y_resolution=0.01)

        self.averaging_label = QLabel('Averaging:')
        self.averaging_combo = QComboBox()
        self.averaging_combo.addItem('1', BrickletThermocouple.AVERAGING_1)
        self.averaging_combo.addItem('2', BrickletThermocouple.AVERAGING_2)
        self.averaging_combo.addItem('4', BrickletThermocouple.AVERAGING_4)
        self.averaging_combo.addItem('8', BrickletThermocouple.AVERAGING_8)
        self.averaging_combo.addItem('16', BrickletThermocouple.AVERAGING_16)

        self.type_label = QLabel('Thermocouple Type:')
        self.type_combo = QComboBox()
        self.type_combo.addItem('B', BrickletThermocouple.TYPE_B)
        self.type_combo.addItem('E', BrickletThermocouple.TYPE_E)
        self.type_combo.addItem('J', BrickletThermocouple.TYPE_J)
        self.type_combo.addItem('K', BrickletThermocouple.TYPE_K)
        self.type_combo.addItem('N', BrickletThermocouple.TYPE_N)
        self.type_combo.addItem('R', BrickletThermocouple.TYPE_R)
        self.type_combo.addItem('S', BrickletThermocouple.TYPE_S)
        self.type_combo.addItem('T', BrickletThermocouple.TYPE_T)
        self.type_combo.addItem('Gain 8', BrickletThermocouple.TYPE_G8)
        self.type_combo.addItem('Gain 32', BrickletThermocouple.TYPE_G32)

        self.filter_label = QLabel('Noise Rejection Filter:')
        self.filter_combo = QComboBox()
        self.filter_combo.addItem('50 Hz', BrickletThermocouple.FILTER_OPTION_50HZ)
        self.filter_combo.addItem('60 Hz', BrickletThermocouple.FILTER_OPTION_60HZ)

        hlayout = QHBoxLayout()
        hlayout.addWidget(self.averaging_label)
        hlayout.addWidget(self.averaging_combo)
        hlayout.addStretch()
        hlayout.addWidget(self.type_label)
        hlayout.addWidget(self.type_combo)
        hlayout.addStretch()
        hlayout.addWidget(self.filter_label)
        hlayout.addWidget(self.filter_combo)

        line = QFrame()
        line.setObjectName("line")
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)

        layout = QVBoxLayout(self)
        layout.addWidget(self.plot_widget)
        layout.addWidget(line)
        layout.addLayout(hlayout)

        self.averaging_combo.currentIndexChanged.connect(self.configuration_changed)
        self.type_combo.currentIndexChanged.connect(self.configuration_changed)
        self.filter_combo.currentIndexChanged.connect(self.configuration_changed)

    def start(self):
        async_call(self.thermo.get_configuration, None, self.get_configuration_async, self.increase_error_count)
        async_call(self.thermo.get_error_state, None, self.cb_error_state, self.increase_error_count,
                   expand_result_tuple_for_callback=True)

        self.cbe_temperature.set_period(100)

        self.plot_widget.stop = False

    def stop(self):
        self.cbe_temperature.set_period(0)

        self.plot_widget.stop = True

    def destroy(self):
        pass

    @staticmethod
    def has_device_identifier(device_identifier):
        return device_identifier == BrickletThermocouple.DEVICE_IDENTIFIER

    def configuration_changed(self, _):
        conf_averaging = self.averaging_combo.itemData(self.averaging_combo.currentIndex())
        conf_type = self.type_combo.itemData(self.type_combo.currentIndex())
        conf_filter = self.filter_combo.itemData(self.filter_combo.currentIndex())

        self.thermo.set_configuration(conf_averaging, conf_type, conf_filter)

    def cb_temperature(self, temperature):
        self.current_temperature.value = temperature / 100.0

    def get_configuration_async(self, conf):
        self.averaging_combo.blockSignals(True)
        self.averaging_combo.setCurrentIndex(self.averaging_combo.findData(conf.averaging))
        self.averaging_combo.blockSignals(False)

        self.type_combo.blockSignals(True)
        self.type_combo.setCurrentIndex(self.type_combo.findData(conf.thermocouple_type))
        self.type_combo.blockSignals(False)

        self.filter_combo.blockSignals(True)
        self.filter_combo.setCurrentIndex(self.filter_combo.findData(conf.filter))
        self.filter_combo.blockSignals(False)

    def cb_error_state(self, over_under, open_circuit):
        if over_under or open_circuit:
            text = 'Current Errors: '

            if over_under:
                text += 'Over/Under Voltage'

            if over_under and open_circuit:
                text += ' and '

            if open_circuit:
                text += 'Open Circuit\n(defective thermocouple or nothing connected)'

            self.error_label.setStyleSheet('QLabel { color : red }')
            self.error_label.setText(text)
        else:
            self.error_label.setStyleSheet('')
            self.error_label.setText('Current Errors: None')
Exemple #30
0
class RegExpDialog(QDialog):
    MaxCaptures = 6

    def __init__(self, parent=None):
        super(RegExpDialog, self).__init__(parent)

        self.patternComboBox = QComboBox()
        self.patternComboBox.setEditable(True)
        self.patternComboBox.setSizePolicy(QSizePolicy.Expanding,
                                           QSizePolicy.Preferred)

        patternLabel = QLabel("&Pattern:")
        patternLabel.setBuddy(self.patternComboBox)

        self.escapedPatternLineEdit = QLineEdit()
        self.escapedPatternLineEdit.setReadOnly(True)
        palette = self.escapedPatternLineEdit.palette()
        palette.setBrush(QPalette.Base,
                         palette.brush(QPalette.Disabled, QPalette.Base))
        self.escapedPatternLineEdit.setPalette(palette)

        escapedPatternLabel = QLabel("&Escaped Pattern:")
        escapedPatternLabel.setBuddy(self.escapedPatternLineEdit)

        self.syntaxComboBox = QComboBox()
        self.syntaxComboBox.addItem("Regular expression v1", QRegExp.RegExp)
        self.syntaxComboBox.addItem("Regular expression v2", QRegExp.RegExp2)
        self.syntaxComboBox.addItem("Wildcard", QRegExp.Wildcard)
        self.syntaxComboBox.addItem("Fixed string", QRegExp.FixedString)

        syntaxLabel = QLabel("&Pattern Syntax:")
        syntaxLabel.setBuddy(self.syntaxComboBox)

        self.textComboBox = QComboBox()
        self.textComboBox.setEditable(True)
        self.textComboBox.setSizePolicy(QSizePolicy.Expanding,
                                        QSizePolicy.Preferred)

        textLabel = QLabel("&Text:")
        textLabel.setBuddy(self.textComboBox)

        self.caseSensitiveCheckBox = QCheckBox("Case &Sensitive")
        self.caseSensitiveCheckBox.setChecked(True)
        self.minimalCheckBox = QCheckBox("&Minimal")

        indexLabel = QLabel("Index of Match:")
        self.indexEdit = QLineEdit()
        self.indexEdit.setReadOnly(True)

        matchedLengthLabel = QLabel("Matched Length:")
        self.matchedLengthEdit = QLineEdit()
        self.matchedLengthEdit.setReadOnly(True)

        self.captureLabels = []
        self.captureEdits = []
        for i in range(self.MaxCaptures):
            self.captureLabels.append(QLabel("Capture %d:" % i))
            self.captureEdits.append(QLineEdit())
            self.captureEdits[i].setReadOnly(True)
        self.captureLabels[0].setText("Match:")

        checkBoxLayout = QHBoxLayout()
        checkBoxLayout.addWidget(self.caseSensitiveCheckBox)
        checkBoxLayout.addWidget(self.minimalCheckBox)
        checkBoxLayout.addStretch(1)

        mainLayout = QGridLayout()
        mainLayout.addWidget(patternLabel, 0, 0)
        mainLayout.addWidget(self.patternComboBox, 0, 1)
        mainLayout.addWidget(escapedPatternLabel, 1, 0)
        mainLayout.addWidget(self.escapedPatternLineEdit, 1, 1)
        mainLayout.addWidget(syntaxLabel, 2, 0)
        mainLayout.addWidget(self.syntaxComboBox, 2, 1)
        mainLayout.addLayout(checkBoxLayout, 3, 0, 1, 2)
        mainLayout.addWidget(textLabel, 4, 0)
        mainLayout.addWidget(self.textComboBox, 4, 1)
        mainLayout.addWidget(indexLabel, 5, 0)
        mainLayout.addWidget(self.indexEdit, 5, 1)
        mainLayout.addWidget(matchedLengthLabel, 6, 0)
        mainLayout.addWidget(self.matchedLengthEdit, 6, 1)

        for i in range(self.MaxCaptures):
            mainLayout.addWidget(self.captureLabels[i], 7 + i, 0)
            mainLayout.addWidget(self.captureEdits[i], 7 + i, 1)
        self.setLayout(mainLayout)

        self.patternComboBox.editTextChanged.connect(self.refresh)
        self.textComboBox.editTextChanged.connect(self.refresh)
        self.caseSensitiveCheckBox.toggled.connect(self.refresh)
        self.minimalCheckBox.toggled.connect(self.refresh)
        self.syntaxComboBox.currentIndexChanged.connect(self.refresh)

        self.patternComboBox.addItem("[A-Za-z_]+([A-Za-z_0-9]*)")
        self.textComboBox.addItem("(10 + delta4)* 32")

        self.setWindowTitle("RegExp")
        self.setFixedHeight(self.sizeHint().height())
        self.refresh()

    def refresh(self):
        self.setUpdatesEnabled(False)

        pattern = self.patternComboBox.currentText()
        text = self.textComboBox.currentText()

        escaped = str(pattern)
        escaped.replace("\\", "\\\\")
        escaped.replace('"', '\\"')
        self.escapedPatternLineEdit.setText('"' + escaped + '"')

        rx = QRegExp(pattern)
        cs = (Qt.CaseSensitive if self.caseSensitiveCheckBox.isChecked() else
              Qt.CaseInsensitive)
        rx.setCaseSensitivity(cs)
        rx.setMinimal(self.minimalCheckBox.isChecked())
        syntax = self.syntaxComboBox.itemData(
            self.syntaxComboBox.currentIndex())
        rx.setPatternSyntax(syntax)

        palette = self.patternComboBox.palette()
        if rx.isValid():
            palette.setColor(QPalette.Text,
                             self.textComboBox.palette().color(QPalette.Text))
        else:
            palette.setColor(QPalette.Text, Qt.red)
        self.patternComboBox.setPalette(palette)

        self.indexEdit.setText(str(rx.indexIn(text)))
        self.matchedLengthEdit.setText(str(rx.matchedLength()))

        for i in range(self.MaxCaptures):
            self.captureLabels[i].setEnabled(i <= rx.captureCount())
            self.captureEdits[i].setEnabled(i <= rx.captureCount())
            self.captureEdits[i].setText(rx.cap(i))

        self.setUpdatesEnabled(True)
Exemple #31
0
    def __init__(self, parent: 'ElectrumWindow', config: 'SimpleConfig'):
        WindowModalDialog.__init__(self, parent, _('Preferences'))
        self.config = config
        self.window = parent
        self.need_restart = False
        self.fx = self.window.fx
        self.wallet = self.window.wallet
        
        vbox = QVBoxLayout()
        tabs = QTabWidget()
        gui_widgets = []
        tx_widgets = []
        oa_widgets = []

        # language
        lang_help = _('Select which language is used in the GUI (after restart).')
        lang_label = HelpLabel(_('Language') + ':', lang_help)
        lang_combo = QComboBox()
        lang_combo.addItems(list(languages.values()))
        lang_keys = list(languages.keys())
        lang_cur_setting = self.config.get("language", '')
        try:
            index = lang_keys.index(lang_cur_setting)
        except ValueError:  # not in list
            index = 0
        lang_combo.setCurrentIndex(index)
        if not self.config.is_modifiable('language'):
            for w in [lang_combo, lang_label]: w.setEnabled(False)
        def on_lang(x):
            lang_request = list(languages.keys())[lang_combo.currentIndex()]
            if lang_request != self.config.get('language'):
                self.config.set_key("language", lang_request, True)
                self.need_restart = True
        lang_combo.currentIndexChanged.connect(on_lang)
        gui_widgets.append((lang_label, lang_combo))

        nz_help = _('Number of zeros displayed after the decimal point. For example, if this is set to 2, "1." will be displayed as "1.00"')
        nz_label = HelpLabel(_('Zeros after decimal point') + ':', nz_help)
        nz = QSpinBox()
        nz.setMinimum(0)
        nz.setMaximum(self.config.decimal_point)
        nz.setValue(self.config.num_zeros)
        if not self.config.is_modifiable('num_zeros'):
            for w in [nz, nz_label]: w.setEnabled(False)
        def on_nz():
            value = nz.value()
            if self.config.num_zeros != value:
                self.config.num_zeros = value
                self.config.set_key('num_zeros', value, True)
                self.window.history_list.update()
                self.window.address_list.update()
        nz.valueChanged.connect(on_nz)
        gui_widgets.append((nz_label, nz))

        use_rbf = bool(self.config.get('use_rbf', True))
        use_rbf_cb = QCheckBox(_('Use Replace-By-Fee'))
        use_rbf_cb.setChecked(use_rbf)
        use_rbf_cb.setToolTip(
            _('If you check this box, your transactions will be marked as non-final,') + '\n' + \
            _('and you will have the possibility, while they are unconfirmed, to replace them with transactions that pay higher fees.') + '\n' + \
            _('Note that some merchants do not accept non-final transactions until they are confirmed.'))
        def on_use_rbf(x):
            self.config.set_key('use_rbf', bool(x))
            batch_rbf_cb.setEnabled(bool(x))
        use_rbf_cb.stateChanged.connect(on_use_rbf)
        tx_widgets.append((use_rbf_cb, None))

        batch_rbf_cb = QCheckBox(_('Batch RBF transactions'))
        batch_rbf_cb.setChecked(bool(self.config.get('batch_rbf', False)))
        batch_rbf_cb.setEnabled(use_rbf)
        batch_rbf_cb.setToolTip(
            _('If you check this box, your unconfirmed transactions will be consolidated into a single transaction.') + '\n' + \
            _('This will save fees.'))
        def on_batch_rbf(x):
            self.config.set_key('batch_rbf', bool(x))
        batch_rbf_cb.stateChanged.connect(on_batch_rbf)
        tx_widgets.append((batch_rbf_cb, None))

        # lightning
        lightning_widgets = []

        if self.wallet.lnworker and self.wallet.lnworker.has_deterministic_node_id():
            help_recov = _(messages.MSG_RECOVERABLE_CHANNELS)
            recov_cb = QCheckBox(_("Create recoverable channels"))
            recov_cb.setToolTip(help_recov)
            recov_cb.setChecked(bool(self.config.get('use_recoverable_channels', True)))
            def on_recov_checked(x):
                self.config.set_key('use_recoverable_channels', bool(x))
            recov_cb.stateChanged.connect(on_recov_checked)
            recov_cb.setEnabled(not bool(self.config.get('lightning_listen')))
            lightning_widgets.append((recov_cb, None))

        help_gossip = _("""If this option is enabled, Electrum will download the network
channels graph and compute payment path locally, instead of using trampoline payments. """)
        gossip_cb = QCheckBox(_("Enable gossip (disable trampoline)"))
        gossip_cb.setToolTip(help_gossip)
        gossip_cb.setChecked(bool(self.config.get('use_gossip', False)))
        def on_gossip_checked(x):
            use_gossip = bool(x)
            self.config.set_key('use_gossip', use_gossip)
            if use_gossip:
                self.window.network.start_gossip()
            else:
                self.window.network.run_from_another_thread(
                    self.window.network.stop_gossip())
            util.trigger_callback('ln_gossip_sync_progress')
            # FIXME: update all wallet windows
            util.trigger_callback('channels_updated', self.wallet)
        gossip_cb.stateChanged.connect(on_gossip_checked)
        lightning_widgets.append((gossip_cb, None))

        help_local_wt = _("""If this option is checked, Electrum will
run a local watchtower and protect your channels even if your wallet is not
open. For this to work, your computer needs to be online regularly.""")
        local_wt_cb = QCheckBox(_("Run a local watchtower"))
        local_wt_cb.setToolTip(help_local_wt)
        local_wt_cb.setChecked(bool(self.config.get('run_local_watchtower', False)))
        def on_local_wt_checked(x):
            self.config.set_key('run_local_watchtower', bool(x))
        local_wt_cb.stateChanged.connect(on_local_wt_checked)
        lightning_widgets.append((local_wt_cb, None))

        help_persist = _("""If this option is checked, Electrum will persist after
you close all your wallet windows, and the Electrum icon will be visible in the taskbar.
Use this if you want your local watchtower to keep running after you close your wallet.""")
        persist_cb = QCheckBox(_("Persist after all windows are closed"))
        persist_cb.setToolTip(help_persist)
        persist_cb.setChecked(bool(self.config.get('persist_daemon', False)))
        def on_persist_checked(x):
            self.config.set_key('persist_daemon', bool(x))
        persist_cb.stateChanged.connect(on_persist_checked)
        lightning_widgets.append((persist_cb, None))

        help_remote_wt = _("""To use a remote watchtower, enter the corresponding URL here""")
        remote_wt_cb = QCheckBox(_("Use a remote watchtower"))
        remote_wt_cb.setToolTip(help_remote_wt)
        remote_wt_cb.setChecked(bool(self.config.get('use_watchtower', False)))
        def on_remote_wt_checked(x):
            self.config.set_key('use_watchtower', bool(x))
            self.watchtower_url_e.setEnabled(bool(x))
        remote_wt_cb.stateChanged.connect(on_remote_wt_checked)
        watchtower_url = self.config.get('watchtower_url')
        self.watchtower_url_e = QLineEdit(watchtower_url)
        self.watchtower_url_e.setEnabled(self.config.get('use_watchtower', False))
        def on_wt_url():
            url = self.watchtower_url_e.text() or None
            watchtower_url = self.config.set_key('watchtower_url', url)
        self.watchtower_url_e.editingFinished.connect(on_wt_url)
        lightning_widgets.append((remote_wt_cb, self.watchtower_url_e))

        msg = _('OpenAlias record, used to receive coins and to sign payment requests.') + '\n\n'\
              + _('The following alias providers are available:') + '\n'\
              + '\n'.join(['https://cryptoname.co/', 'http://xmr.link']) + '\n\n'\
              + 'For more information, see https://openalias.org'
        alias_label = HelpLabel(_('OpenAlias') + ':', msg)
        alias = self.config.get('alias','')
        self.alias_e = QLineEdit(alias)
        self.set_alias_color()
        self.alias_e.editingFinished.connect(self.on_alias_edit)
        oa_widgets.append((alias_label, self.alias_e))

        # units
        units = base_units_list
        msg = (_('Base unit of your wallet.')
               + '\n1 BTC = 1000 mBTC. 1 mBTC = 1000 bits. 1 bit = 100 sat.\n'
               + _('This setting affects the Send tab, and all balance related fields.'))
        unit_label = HelpLabel(_('Base unit') + ':', msg)
        unit_combo = QComboBox()
        unit_combo.addItems(units)
        unit_combo.setCurrentIndex(units.index(self.window.base_unit()))
        def on_unit(x, nz):
            unit_result = units[unit_combo.currentIndex()]
            if self.window.base_unit() == unit_result:
                return
            edits = self.window.amount_e, self.window.receive_amount_e
            amounts = [edit.get_amount() for edit in edits]
            self.config.set_base_unit(unit_result)
            nz.setMaximum(self.config.decimal_point)
            self.window.update_tabs()
            for edit, amount in zip(edits, amounts):
                edit.setAmount(amount)
            self.window.update_status()
        unit_combo.currentIndexChanged.connect(lambda x: on_unit(x, nz))
        gui_widgets.append((unit_label, unit_combo))

        system_cameras = qrscanner._find_system_cameras()
        qr_combo = QComboBox()
        qr_combo.addItem("Default","default")
        for camera, device in system_cameras.items():
            qr_combo.addItem(camera, device)
        #combo.addItem("Manually specify a device", config.get("video_device"))
        index = qr_combo.findData(self.config.get("video_device"))
        qr_combo.setCurrentIndex(index)
        msg = _("Install the zbar package to enable this.")
        qr_label = HelpLabel(_('Video Device') + ':', msg)
        qr_combo.setEnabled(qrscanner.libzbar is not None)
        on_video_device = lambda x: self.config.set_key("video_device", qr_combo.itemData(x), True)
        qr_combo.currentIndexChanged.connect(on_video_device)
        gui_widgets.append((qr_label, qr_combo))

        colortheme_combo = QComboBox()
        colortheme_combo.addItem(_('Light'), 'default')
        colortheme_combo.addItem(_('Dark'), 'dark')
        index = colortheme_combo.findData(self.config.get('qt_gui_color_theme', 'default'))
        colortheme_combo.setCurrentIndex(index)
        colortheme_label = QLabel(_('Color theme') + ':')
        def on_colortheme(x):
            self.config.set_key('qt_gui_color_theme', colortheme_combo.itemData(x), True)
            self.need_restart = True
        colortheme_combo.currentIndexChanged.connect(on_colortheme)
        gui_widgets.append((colortheme_label, colortheme_combo))

        updatecheck_cb = QCheckBox(_("Automatically check for software updates"))
        updatecheck_cb.setChecked(bool(self.config.get('check_updates', False)))
        def on_set_updatecheck(v):
            self.config.set_key('check_updates', v == Qt.Checked, save=True)
        updatecheck_cb.stateChanged.connect(on_set_updatecheck)
        gui_widgets.append((updatecheck_cb, None))

        filelogging_cb = QCheckBox(_("Write logs to file"))
        filelogging_cb.setChecked(bool(self.config.get('log_to_file', False)))
        def on_set_filelogging(v):
            self.config.set_key('log_to_file', v == Qt.Checked, save=True)
            self.need_restart = True
        filelogging_cb.stateChanged.connect(on_set_filelogging)
        filelogging_cb.setToolTip(_('Debug logs can be persisted to disk. These are useful for troubleshooting.'))
        gui_widgets.append((filelogging_cb, None))

        preview_cb = QCheckBox(_('Advanced preview'))
        preview_cb.setChecked(bool(self.config.get('advanced_preview', False)))
        preview_cb.setToolTip(_("Open advanced transaction preview dialog when 'Pay' is clicked."))
        def on_preview(x):
            self.config.set_key('advanced_preview', x == Qt.Checked)
        preview_cb.stateChanged.connect(on_preview)
        tx_widgets.append((preview_cb, None))

        usechange_cb = QCheckBox(_('Use change addresses'))
        usechange_cb.setChecked(self.window.wallet.use_change)
        if not self.config.is_modifiable('use_change'): usechange_cb.setEnabled(False)
        def on_usechange(x):
            usechange_result = x == Qt.Checked
            if self.window.wallet.use_change != usechange_result:
                self.window.wallet.use_change = usechange_result
                self.window.wallet.db.put('use_change', self.window.wallet.use_change)
                multiple_cb.setEnabled(self.window.wallet.use_change)
        usechange_cb.stateChanged.connect(on_usechange)
        usechange_cb.setToolTip(_('Using change addresses makes it more difficult for other people to track your transactions.'))
        tx_widgets.append((usechange_cb, None))

        def on_multiple(x):
            multiple = x == Qt.Checked
            if self.wallet.multiple_change != multiple:
                self.wallet.multiple_change = multiple
                self.wallet.db.put('multiple_change', multiple)
        multiple_change = self.wallet.multiple_change
        multiple_cb = QCheckBox(_('Use multiple change addresses'))
        multiple_cb.setEnabled(self.wallet.use_change)
        multiple_cb.setToolTip('\n'.join([
            _('In some cases, use up to 3 change addresses in order to break '
              'up large coin amounts and obfuscate the recipient address.'),
            _('This may result in higher transactions fees.')
        ]))
        multiple_cb.setChecked(multiple_change)
        multiple_cb.stateChanged.connect(on_multiple)
        tx_widgets.append((multiple_cb, None))

        def fmt_docs(key, klass):
            lines = [ln.lstrip(" ") for ln in klass.__doc__.split("\n")]
            return '\n'.join([key, "", " ".join(lines)])

        choosers = sorted(coinchooser.COIN_CHOOSERS.keys())
        if len(choosers) > 1:
            chooser_name = coinchooser.get_name(self.config)
            msg = _('Choose coin (UTXO) selection method.  The following are available:\n\n')
            msg += '\n\n'.join(fmt_docs(*item) for item in coinchooser.COIN_CHOOSERS.items())
            chooser_label = HelpLabel(_('Coin selection') + ':', msg)
            chooser_combo = QComboBox()
            chooser_combo.addItems(choosers)
            i = choosers.index(chooser_name) if chooser_name in choosers else 0
            chooser_combo.setCurrentIndex(i)
            def on_chooser(x):
                chooser_name = choosers[chooser_combo.currentIndex()]
                self.config.set_key('coin_chooser', chooser_name)
            chooser_combo.currentIndexChanged.connect(on_chooser)
            tx_widgets.append((chooser_label, chooser_combo))

        def on_unconf(x):
            self.config.set_key('confirmed_only', bool(x))
        conf_only = bool(self.config.get('confirmed_only', False))
        unconf_cb = QCheckBox(_('Spend only confirmed coins'))
        unconf_cb.setToolTip(_('Spend only confirmed inputs.'))
        unconf_cb.setChecked(conf_only)
        unconf_cb.stateChanged.connect(on_unconf)
        tx_widgets.append((unconf_cb, None))

        def on_outrounding(x):
            self.config.set_key('coin_chooser_output_rounding', bool(x))
        enable_outrounding = bool(self.config.get('coin_chooser_output_rounding', True))
        outrounding_cb = QCheckBox(_('Enable output value rounding'))
        outrounding_cb.setToolTip(
            _('Set the value of the change output so that it has similar precision to the other outputs.') + '\n' +
            _('This might improve your privacy somewhat.') + '\n' +
            _('If enabled, at most 100 satoshis might be lost due to this, per transaction.'))
        outrounding_cb.setChecked(enable_outrounding)
        outrounding_cb.stateChanged.connect(on_outrounding)
        tx_widgets.append((outrounding_cb, None))

        block_explorers = sorted(util.block_explorer_info().keys())
        BLOCK_EX_CUSTOM_ITEM = _("Custom URL")
        if BLOCK_EX_CUSTOM_ITEM in block_explorers:  # malicious translation?
            block_explorers.remove(BLOCK_EX_CUSTOM_ITEM)
        block_explorers.append(BLOCK_EX_CUSTOM_ITEM)
        msg = _('Choose which online block explorer to use for functions that open a web browser')
        block_ex_label = HelpLabel(_('Online Block Explorer') + ':', msg)
        block_ex_combo = QComboBox()
        block_ex_custom_e = QLineEdit(self.config.get('block_explorer_custom') or '')
        block_ex_combo.addItems(block_explorers)
        block_ex_combo.setCurrentIndex(
            block_ex_combo.findText(util.block_explorer(self.config) or BLOCK_EX_CUSTOM_ITEM))
        def showhide_block_ex_custom_e():
            block_ex_custom_e.setVisible(block_ex_combo.currentText() == BLOCK_EX_CUSTOM_ITEM)
        showhide_block_ex_custom_e()
        def on_be_combo(x):
            if block_ex_combo.currentText() == BLOCK_EX_CUSTOM_ITEM:
                on_be_edit()
            else:
                be_result = block_explorers[block_ex_combo.currentIndex()]
                self.config.set_key('block_explorer_custom', None, False)
                self.config.set_key('block_explorer', be_result, True)
            showhide_block_ex_custom_e()
        block_ex_combo.currentIndexChanged.connect(on_be_combo)
        def on_be_edit():
            val = block_ex_custom_e.text()
            try:
                val = ast.literal_eval(val)  # to also accept tuples
            except:
                pass
            self.config.set_key('block_explorer_custom', val)
        block_ex_custom_e.editingFinished.connect(on_be_edit)
        block_ex_hbox = QHBoxLayout()
        block_ex_hbox.setContentsMargins(0, 0, 0, 0)
        block_ex_hbox.setSpacing(0)
        block_ex_hbox.addWidget(block_ex_combo)
        block_ex_hbox.addWidget(block_ex_custom_e)
        block_ex_hbox_w = QWidget()
        block_ex_hbox_w.setLayout(block_ex_hbox)
        tx_widgets.append((block_ex_label, block_ex_hbox_w))

        # Fiat Currency
        hist_checkbox = QCheckBox()
        hist_capgains_checkbox = QCheckBox()
        fiat_address_checkbox = QCheckBox()
        ccy_combo = QComboBox()
        ex_combo = QComboBox()

        def update_currencies():
            if not self.window.fx: return
            currencies = sorted(self.fx.get_currencies(self.fx.get_history_config()))
            ccy_combo.clear()
            ccy_combo.addItems([_('None')] + currencies)
            if self.fx.is_enabled():
                ccy_combo.setCurrentIndex(ccy_combo.findText(self.fx.get_currency()))

        def update_history_cb():
            if not self.fx: return
            hist_checkbox.setChecked(self.fx.get_history_config())
            hist_checkbox.setEnabled(self.fx.is_enabled())

        def update_fiat_address_cb():
            if not self.fx: return
            fiat_address_checkbox.setChecked(self.fx.get_fiat_address_config())

        def update_history_capgains_cb():
            if not self.fx: return
            hist_capgains_checkbox.setChecked(self.fx.get_history_capital_gains_config())
            hist_capgains_checkbox.setEnabled(hist_checkbox.isChecked())

        def update_exchanges():
            if not self.fx: return
            b = self.fx.is_enabled()
            ex_combo.setEnabled(b)
            if b:
                h = self.fx.get_history_config()
                c = self.fx.get_currency()
                exchanges = self.fx.get_exchanges_by_ccy(c, h)
            else:
                exchanges = self.fx.get_exchanges_by_ccy('USD', False)
            ex_combo.blockSignals(True)
            ex_combo.clear()
            ex_combo.addItems(sorted(exchanges))
            ex_combo.setCurrentIndex(ex_combo.findText(self.fx.config_exchange()))
            ex_combo.blockSignals(False)

        def on_currency(hh):
            if not self.fx: return
            b = bool(ccy_combo.currentIndex())
            ccy = str(ccy_combo.currentText()) if b else None
            self.fx.set_enabled(b)
            if b and ccy != self.fx.ccy:
                self.fx.set_currency(ccy)
            update_history_cb()
            update_exchanges()
            self.window.update_fiat()

        def on_exchange(idx):
            exchange = str(ex_combo.currentText())
            if self.fx and self.fx.is_enabled() and exchange and exchange != self.fx.exchange.name():
                self.fx.set_exchange(exchange)

        def on_history(checked):
            if not self.fx: return
            self.fx.set_history_config(checked)
            update_exchanges()
            self.window.history_model.refresh('on_history')
            if self.fx.is_enabled() and checked:
                self.fx.trigger_update()
            update_history_capgains_cb()

        def on_history_capgains(checked):
            if not self.fx: return
            self.fx.set_history_capital_gains_config(checked)
            self.window.history_model.refresh('on_history_capgains')

        def on_fiat_address(checked):
            if not self.fx: return
            self.fx.set_fiat_address_config(checked)
            self.window.address_list.refresh_headers()
            self.window.address_list.update()

        update_currencies()
        update_history_cb()
        update_history_capgains_cb()
        update_fiat_address_cb()
        update_exchanges()
        ccy_combo.currentIndexChanged.connect(on_currency)
        hist_checkbox.stateChanged.connect(on_history)
        hist_capgains_checkbox.stateChanged.connect(on_history_capgains)
        fiat_address_checkbox.stateChanged.connect(on_fiat_address)
        ex_combo.currentIndexChanged.connect(on_exchange)

        fiat_widgets = []
        fiat_widgets.append((QLabel(_('Fiat currency')), ccy_combo))
        fiat_widgets.append((QLabel(_('Source')), ex_combo))
        fiat_widgets.append((QLabel(_('Show history rates')), hist_checkbox))
        fiat_widgets.append((QLabel(_('Show capital gains in history')), hist_capgains_checkbox))
        fiat_widgets.append((QLabel(_('Show Fiat balance for addresses')), fiat_address_checkbox))

        tabs_info = [
            (gui_widgets, _('General')),
            (tx_widgets, _('Transactions')),
            (lightning_widgets, _('Lightning')),
            (fiat_widgets, _('Fiat')),
            (oa_widgets, _('OpenAlias')),
        ]
        for widgets, name in tabs_info:
            tab = QWidget()
            tab_vbox = QVBoxLayout(tab)
            grid = QGridLayout()
            for a,b in widgets:
                i = grid.rowCount()
                if b:
                    if a:
                        grid.addWidget(a, i, 0)
                    grid.addWidget(b, i, 1)
                else:
                    grid.addWidget(a, i, 0, 1, 2)
            tab_vbox.addLayout(grid)
            tab_vbox.addStretch(1)
            tabs.addTab(tab, name)

        vbox.addWidget(tabs)
        vbox.addStretch(1)
        vbox.addLayout(Buttons(CloseButton(self)))
        self.setLayout(vbox)
class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()

        self.proxyModel = SortFilterProxyModel()
        self.proxyModel.setDynamicSortFilter(True)

        self.sourceGroupBox = QGroupBox("Original Model")
        self.proxyGroupBox = QGroupBox("Sorted/Filtered Model")

        self.sourceView = QTreeView()
        self.sourceView.setRootIsDecorated(False)
        self.sourceView.setAlternatingRowColors(True)

        self.proxyView = QTreeView()
        self.proxyView.setRootIsDecorated(False)
        self.proxyView.setAlternatingRowColors(True)
        self.proxyView.setModel(self.proxyModel)
        self.proxyView.setSortingEnabled(True)

        self.sortCaseSensitivityCheckBox = QCheckBox("Case sensitive sorting")
        self.filterCaseSensitivityCheckBox = QCheckBox("Case sensitive filter")

        self.filterPatternLineEdit = QLineEdit()
        self.filterPatternLabel = QLabel("&Filter pattern:")
        self.filterPatternLabel.setBuddy(self.filterPatternLineEdit)

        self.filterSyntaxComboBox = QComboBox()
        self.filterSyntaxComboBox.addItem("Regular expression", QRegExp.RegExp)
        self.filterSyntaxComboBox.addItem("Wildcard", QRegExp.Wildcard)
        self.filterSyntaxComboBox.addItem("Fixed string", QRegExp.FixedString)
        self.filterSyntaxLabel = QLabel("Filter &syntax:")
        self.filterSyntaxLabel.setBuddy(self.filterSyntaxComboBox)

        self.filterColumnComboBox = QComboBox()
        self.filterColumnComboBox.addItem("Subject")
        self.filterColumnComboBox.addItem("Sender")
        self.filterColumnComboBox.addItem("Date")
        self.filterColumnLabel = QLabel("Filter &column:")
        self.filterColumnLabel.setBuddy(self.filterColumnComboBox)

        self.filterPatternLineEdit.textChanged.connect(
            self.filterRegExpChanged)
        self.filterSyntaxComboBox.currentIndexChanged.connect(
            self.filterRegExpChanged)
        self.filterColumnComboBox.currentIndexChanged.connect(
            self.filterColumnChanged)
        self.filterCaseSensitivityCheckBox.toggled.connect(
            self.filterRegExpChanged)
        self.sortCaseSensitivityCheckBox.toggled.connect(self.sortChanged)

        sourceLayout = QHBoxLayout()
        sourceLayout.addWidget(self.sourceView)
        self.sourceGroupBox.setLayout(sourceLayout)

        proxyLayout = QGridLayout()
        proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3)
        proxyLayout.addWidget(self.filterPatternLabel, 1, 0)
        proxyLayout.addWidget(self.filterPatternLineEdit, 1, 1, 1, 2)
        proxyLayout.addWidget(self.filterSyntaxLabel, 2, 0)
        proxyLayout.addWidget(self.filterSyntaxComboBox, 2, 1, 1, 2)
        proxyLayout.addWidget(self.filterColumnLabel, 3, 0)
        proxyLayout.addWidget(self.filterColumnComboBox, 3, 1, 1, 2)
        proxyLayout.addWidget(self.filterCaseSensitivityCheckBox, 4, 0, 1, 2)
        proxyLayout.addWidget(self.sortCaseSensitivityCheckBox, 4, 2)
        self.proxyGroupBox.setLayout(proxyLayout)

        mainLayout = QVBoxLayout()
        mainLayout.addWidget(self.sourceGroupBox)
        mainLayout.addWidget(self.proxyGroupBox)
        self.setLayout(mainLayout)

        self.setWindowTitle("Basic Sort/Filter Model")
        self.resize(500, 450)

        self.proxyView.sortByColumn(SENDER, Qt.AscendingOrder)
        self.filterColumnComboBox.setCurrentIndex(SENDER)

        self.filterPatternLineEdit.setText("Andy|Grace")
        self.filterCaseSensitivityCheckBox.setChecked(True)
        self.sortCaseSensitivityCheckBox.setChecked(True)

    def setSourceModel(self, model):
        self.proxyModel.setSourceModel(model)
        self.sourceView.setModel(model)

    def filterRegExpChanged(self):
        syntax_nr = self.filterSyntaxComboBox.itemData(
            self.filterSyntaxComboBox.currentIndex())
        syntax = QRegExp.PatternSyntax(syntax_nr)

        if self.filterCaseSensitivityCheckBox.isChecked():
            caseSensitivity = Qt.CaseSensitive
        else:
            caseSensitivity = Qt.CaseInsensitive

        regExp = QRegExp(self.filterPatternLineEdit.text(), caseSensitivity,
                         syntax)
        self.proxyModel.setFilterRegExp(regExp)

    def filterColumnChanged(self):
        self.proxyModel.setFilterKeyColumn(
            self.filterColumnComboBox.currentIndex())

    def sortChanged(self):
        if self.sortCaseSensitivityCheckBox.isChecked():
            caseSensitivity = Qt.CaseSensitive
        else:
            caseSensitivity = Qt.CaseInsensitive

        self.proxyModel.setSortCaseSensitivity(caseSensitivity)
Exemple #33
0
class AccountAddDialog(QDialog):
    def __init__(self,
                 parent,
                 icons,
                 edit=False,
                 username='',
                 password='',
                 api=''):
        QDialog.__init__(self, parent)
        self.edit = edit

        # Build UI
        layout = QVBoxLayout()

        formlayout = QFormLayout()
        self.lbl_username = QLabel('Username:'******'Password:'******'Request PIN')
        self.api_auth.setTextFormat(QtCore.Qt.RichText)
        self.api_auth.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction)
        self.api_auth.setOpenExternalLinks(True)
        pin_layout.addWidget(self.password)
        pin_layout.addWidget(self.api_auth)

        formlayout.addRow(QLabel('Site:'), self.api)
        formlayout.addRow(self.lbl_username, self.username)
        formlayout.addRow(self.lbl_password, pin_layout)

        bottombox = QDialogButtonBox()
        bottombox.addButton(QDialogButtonBox.Save)
        bottombox.addButton(QDialogButtonBox.Cancel)
        bottombox.accepted.connect(self.validate)
        bottombox.rejected.connect(self.reject)

        # Populate APIs
        for libname, lib in sorted(utils.available_libs.items()):
            self.api.addItem(icons[libname], lib[0], libname)

        if self.edit:
            self.username.setEnabled(False)
            self.api.setCurrentIndex(self.api.findData(api,
                                                       QtCore.Qt.UserRole))
            self.api.setEnabled(False)

        # Finish layouts
        layout.addLayout(formlayout)
        layout.addWidget(bottombox)

        self.setLayout(layout)

    def validate(self):
        if len(self.username.text()) is 0:
            if len(self.password.text()) is 0:
                self._error('Please fill the credentials fields.')
            else:
                self._error('Please fill the username field.')
        elif len(self.password.text()) is 0:
            self._error('Please fill the password/PIN field.')
        else:
            self.accept()

    def s_refresh(self, index):
        if not self.edit:
            self.username.setText("")
            self.password.setText("")

        if pyqt_version is 5:
            apiname = self.api.itemData(index)
        else:
            apiname = str(self.api.itemData(index))
        api = utils.available_libs[apiname]
        if api[2] == utils.LOGIN_OAUTH:
            apiname = str(self.api.itemData(index))
            url = utils.available_libs[apiname][4]
            self.api_auth.setText("<a href=\"{}\">Request PIN</a>".format(url))
            self.api_auth.show()

            self.lbl_username.setText('Name:')
            self.lbl_password.setText('PIN:')
            self.password.setEchoMode(QLineEdit.Normal)
        else:
            self.lbl_username.setText('Username:'******'Password:'******'Error', msg, QMessageBox.Ok)

    @staticmethod
    def do(parent=None,
           icons=None,
           edit=False,
           username='',
           password='',
           api=''):
        dialog = AccountAddDialog(parent, icons, edit, username, password, api)
        result = dialog.exec_()

        if result == QDialog.Accepted:
            currentIndex = dialog.api.currentIndex()
            return (str(dialog.username.text()), str(dialog.password.text()),
                    str(dialog.api.itemData(currentIndex)))
        else:
            return None
class EditorGeneral(QWidget):
    def __init__(self, parent):
        super().__init__(parent)
        self._preferences = parent
        vbox = QVBoxLayout(self)
        self._font = settings.FONT

        # Group widgets
        group_typo = QGroupBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_TYPOGRAPHY)
        group_scheme = QGroupBox("Editor Color Scheme")

        # Font settings
        grid_typo = QGridLayout(group_typo)
        self._btn_editor_font = QPushButton('')
        grid_typo.addWidget(
            QLabel(translations.TR_PREFERENCES_EDITOR_GENERAL_EDITOR_FONT), 0,
            0)
        grid_typo.addWidget(self._btn_editor_font, 0, 1)
        self._check_font_antialiasing = QCheckBox("Antialiasing")
        grid_typo.addWidget(self._check_font_antialiasing, 1, 0)

        # Scheme settings
        box = QVBoxLayout(group_scheme)
        self._combo_themes = QComboBox()
        box.addWidget(self._combo_themes)
        schemes = json_manager.load_editor_schemes()
        for scheme_name, colors in schemes.items():
            self._combo_themes.addItem(scheme_name, colors)
        self.__current_scheme = settings.EDITOR_SCHEME

        #             category_name,

        # Add group widgets
        vbox.addWidget(group_typo)
        vbox.addWidget(group_scheme)
        vbox.addItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding))
        # Initial Settings
        btn_text = ', '.join(self._font.toString().split(',')[0:2])
        self._btn_editor_font.setText(btn_text)
        self._check_font_antialiasing.setChecked(settings.FONT_ANTIALIASING)
        self._combo_themes.setCurrentText(settings.EDITOR_SCHEME)
        # Connections
        self._btn_editor_font.clicked.connect(self._load_editor_font)
        self._preferences.savePreferences.connect(self._save)

    def _load_editor_font(self):
        font, ok = QFontDialog.getFont(self._font, self)
        if ok:
            self._font = font
            btn_text = ', '.join(self._font.toString().split(',')[0:2])
            self._btn_editor_font.setText(btn_text)

    def _save(self):
        qsettings = IDE.editor_settings()

        settings.FONT = self._font
        qsettings.setValue("editor/general/default_font", settings.FONT)
        settings.FONT_ANTIALIASING = self._check_font_antialiasing.isChecked()
        qsettings.setValue("editor/general/font_antialiasing",
                           settings.FONT_ANTIALIASING)
        settings.EDITOR_SCHEME = self._combo_themes.currentText()
        qsettings.setValue("editor/general/scheme", settings.EDITOR_SCHEME)

        scheme = self._combo_themes.currentText()
        if scheme != self.__current_scheme:
            index = self._combo_themes.currentIndex()
            colors = self._combo_themes.itemData(index)
            resources.COLOR_SCHEME = colors
            main = IDE.get_service("main_container")
            main.restyle_editor()
Exemple #35
0
class ConfDialog(QDialog):
    """config dialog"""
    def __init__(self, mainwin, parent=None):
        super(ConfDialog, self).__init__(parent)
        # self.setAttribute(Qt.WA_StyledBackground)
        # self.setAutoFillBackground(True)
        self._app = QApplication.instance()  # 获取app实例
        self.setWindowFlags(Qt.Tool | Qt.WindowStaysOnTopHint)
        self.win = mainwin
        self.leftList = QListWidget()
        self.rightStack = QStackedWidget()
        self.optionActions = {}
        self.changedOptions = {}
        self.initUI()
        self.initConfOptions()
        self.initOptionActions()
        recentlist = self.win.config["file.recent"]
        if recentlist:
            self.win.recent.setList(recentlist)
        self.alertChLang = False

    def initUI(self):
        mainLayout = QVBoxLayout()
        layH1 = QHBoxLayout()
        layH2 = QHBoxLayout()
        layH1.addWidget(self.leftList)
        layH1.addWidget(self.rightStack)
        self.cancelbtn = QPushButton(self.tr("Cancel"))
        self.okbtn = QPushButton(self.tr("OK"))
        # self.cancelbtn.setVisible(False)
        self.okbtn.setDefault(True)
        layH2.addStretch(1)
        layH2.addWidget(self.cancelbtn)
        layH2.addWidget(self.okbtn)
        mainLayout.addLayout(layH1)
        mainLayout.addLayout(layH2)
        self.setLayout(mainLayout)

        # left list
        self.leftList.addItem(self.tr("General"))
        self.leftList.addItem(self.tr("Editor"))
        self.leftList.addItem(self.tr("Update"))
        self.leftList.setMaximumWidth(150)

        # right stack
        w = QWidget()
        layw = QVBoxLayout()
        # general
        g = QGroupBox(self.tr("General"))
        glayout = QFormLayout()
        label1 = QLabel(self.tr("select UI language:"))
        self.langCombo = QComboBox()
        self.fillLangItems(self.langCombo)  # .addItems(self.getLangList())
        self.langCombo.setMinimumWidth(150)
        label2 = QLabel(self.tr("Number of recent files:"))
        self.recentcountspin = QSpinBox()
        self.recentcountspin.setMinimum(1)
        self.recentcountspin.setMaximum(30)
        glayout.addRow(label1, self.langCombo)
        glayout.addRow(label2, self.recentcountspin)
        g.setLayout(glayout)
        layw.addWidget(g)
        # advanced
        g2 = QGroupBox(self.tr("Advanced"))
        labeladv1 = QLabel(self.tr("Export qss When save qsst:"))
        self.checkboxAutoExportQss = QCheckBox()
        self.checkboxAutoExportQss.setToolTip(
            self.
            tr("Option for whether export qss when save qsst file each time."))
        layh1 = QHBoxLayout()
        layh1.addWidget(labeladv1)
        layh1.addStretch(1)
        layh1.addWidget(self.checkboxAutoExportQss)
        g2layout = QVBoxLayout()
        g2layout.addLayout(layh1)
        g2.setLayout(g2layout)
        layw.addWidget(g2)

        # advanced
        g3 = QGroupBox(self.tr("UI Skin"))
        labeladv1 = QLabel(self.tr("Select the ui skin:"))
        self.skinCombo = QComboBox()
        self.skinCombo.setToolTip(self.tr("Select the ui skin."))
        self.skindir = os.path.join(os.path.dirname(__file__), "skin")
        for f in os.listdir(self.skindir):
            if f.endswith(".qss") or f.endswith(".QSS"):
                self.skinCombo.addItem(os.path.basename(f))
        layh1 = QHBoxLayout()
        layh1.addWidget(labeladv1)
        layh1.addStretch(1)
        layh1.addWidget(self.skinCombo)
        labelskin2 = QLabel(self.tr("Manage skins:"))
        skinaddr = QPushButton(self.tr("Skin Management"))
        skinaddr.setToolTip(self.tr("Open the skin directory."))
        skinaddr.clicked.connect(lambda: os.startfile(self.skindir))
        layh2 = QHBoxLayout()
        layh2.addWidget(labelskin2)
        layh2.addStretch(1)
        layh2.addWidget(skinaddr)

        g3layout = QVBoxLayout()
        g3layout.addLayout(layh1)
        g3layout.addLayout(layh2)
        g3.setLayout(g3layout)
        layw.addWidget(g3)

        layw.addStretch(1)
        w.setLayout(layw)
        self.rightStack.addWidget(w)

        # CodeEditor SettingPannel
        self.rightStack.addWidget(self.win.editor.settings.settingPanel())

        # right stack for update
        w3 = QWidget()
        layw3 = QVBoxLayout()
        # update setting
        g31 = QGroupBox(self.tr("update check"))
        self.checkboxUpdate = QCheckBox(self.tr("auto check for update"))
        labtmp = QLabel(self.tr("update checking frequency:"))
        self.updateCombo = QComboBox()
        self.updateCombo.setToolTip(
            self.tr("setup frequency for checking update"))
        self.updateCombo.addItem(self.tr("Each startup"), "start")
        self.updateCombo.addItem(self.tr("Every day"), "day")
        self.updateCombo.addItem(self.tr("Every week"), "week")
        self.updateCombo.addItem(self.tr("Every month"), "month")
        self.updateCombo.setEnabled(False)
        self.checkboxUpdate.stateChanged.connect(self.updateCombo.setEnabled)
        ltmpv = QVBoxLayout()
        ltmph = QHBoxLayout()
        ltmpv.addWidget(self.checkboxUpdate)
        ltmpv.addLayout(ltmph)
        ltmph.addWidget(labtmp)
        ltmph.addStretch(1)
        ltmph.addWidget(self.updateCombo)
        g31.setLayout(ltmpv)
        tmp1 = self.win.config["update.autocheck"]
        tmp2 = self.win.config["update.checkfreq"]
        if not isinstance(tmp1, bool):
            tmp1 = True
        self.checkboxUpdate.setChecked(tmp1)
        if not tmp2:
            tmp2 = "start"
        tmpi = self.updateCombo.findData(tmp2)
        if tmpi:
            self.updateCombo.setCurrentIndex(tmpi)

        layw3.addWidget(g31)
        layw3.addStretch(1)
        w3.setLayout(layw3)
        self.rightStack.addWidget(w3)

        # action for dialog
        self.leftList.currentRowChanged.connect(
            self.rightStack.setCurrentIndex)
        self.cancelbtn.clicked.connect(lambda: (self.cancel(), self.close()))
        self.okbtn.clicked.connect(lambda: (self.apply(), self.close()))

        # actions
        self.recentcountspin.valueChanged.connect(
            lambda x: self.changedOptions.__setitem__("file.recentcount", x))
        self.langCombo.currentIndexChanged.connect(
            lambda i: self.changedOptions.__setitem__(
                "general.language", self.langCombo.itemData(i)))
        self.checkboxUpdate.stateChanged.connect(
            lambda b: self.changedOptions.update({"update.autocheck": b}))
        self.updateCombo.currentIndexChanged.connect(
            lambda i: self.changedOptions.__setitem__(
                "update.checkfreq", self.updateCombo.itemData(i)))
        self.checkboxAutoExportQss.stateChanged.connect(
            lambda b: self.changedOptions.update({"advance.autoexportqss": b}))
        self.skinCombo.currentTextChanged.connect(lambda t: (self.applyskin(
            t), self.changedOptions.update({"general.skin": t})))

    def applyskin(self, skinfile):
        try:
            with open(os.path.join(self.skindir, skinfile),
                      'r',
                      encoding='utf-8') as f:
                self.win.currentUIqss = f.read()
                self.win.setStyleSheet(self.win.currentUIqss)
        except Exception:
            QMessageBox.information(
                self, "Skin Error",
                self.tr("Apply skin error, please check the qss skin."),
                QMessageBox.Ok, QMessageBox.Ok)

    # def showEvent(self, QShowEvent):
    def initConfOptions(self):
        """load option and display on ui when dialog first start"""
        count = self.win.config["file.recentcount"]
        if count:
            self.recentcountspin.setValue(count)
        # lang = self.win.config.get("general.language","en") #self.win.config.getSec("general").get("language", "en")
        # lang = self.win.config["general.language"]
        # if lang is None:
        #     lang = "en"
        skin = self.win.config["general.skin"]
        if not skin:
            skin = "default.qss"
        self.skinCombo.setCurrentText(skin)
        self.applyskin(skin)

        from i18n.language import Language
        lang = Language.lang
        for l in Language.getLangs():
            if l["lang"].replace("-", "_") == lang.replace("-", "_"):
                self.langCombo.setCurrentText(l["nativename"])
                break
        self.checkboxAutoExportQss.setChecked(
            bool(self.win.config["advance.autoexportqss"]))

    def initOptionActions(self):
        self.optionActions = {
            # option: [applyaction, updateUIaction]
            "general.language": [
                lambda l: (self.chLang(l),
                           self.win.config.setChild("general.language", l)),
                self.updateLangCombo
            ],
            "file.recentcount": [
                lambda n:
                (setValue(self.win.recent.maxcount)
                 (n), self.win.config.setChild("file.recentcount", n)),
                self.recentcountspin.setValue
            ],
            "advance.autoexportqss": [
                lambda b: self.win.config.setChild("advance.autoexportqss",
                                                   bool(b)),
                self.checkboxAutoExportQss.setChecked
            ],
            "general.skin": [
                lambda t: (self.win.config.setChild("general.skin", t),
                           self.applyskin(t)), self.skinCombo.setCurrentText
            ],
            "update.autocheck": [
                lambda t: self.win.config.setChild("update.autocheck", bool(
                    t)), self.checkboxUpdate.setChecked
            ],
            "update.checkfreq": [
                lambda t: self.win.config.setChild("update.checkfreq", t),
                lambda t: self.updateCombo.setCurrentIndex(
                    self.updateCombo.findData(t))
            ],
        }

    def fillLangItems(self, combo):
        """set combo list for language
        """
        from i18n.language import Language
        langs = Language.getLangs()
        for l in langs:
            combo.addItem(l["nativename"], l["lang"])
        return True

    def chLang(self, lang="en"):
        """change ui luanguage setting."""
        # print("Change language to "+lang)
        # try:
        #     if lang.lower()=="english":
        #         self._app.removeTranslator(self.win.trans)
        #         return
        #     self.win.trans.load("i18n-"+lang+".qm")
        #     self._app.installTranslator(self.win.trans)
        #     #self._app.retranslateUi(self)# 重新翻译界面
        # except Exception as Argument:
        #     print(Argument)
        lang = self.langCombo.currentData()
        print("Setting Language to " + lang)
        self.win.config["general.language"] = lang
        print("restart soft to enable.")
        if self.alertChLang:
            QMessageBox.information(
                self, self.tr("Change Language Info"),
                self.tr("You must restart soft to enable luanguage change."))

    def updateLangCombo(self, lang=None):
        """update setting ui to lang"""
        from i18n.language import Language
        if not lang:
            lang = Language.lang
        for l in Language.getLangs():
            if l["lang"].replace("-", "_") == lang.replace("-", "_"):
                self.langCombo.setCurrentText(l["nativename"])
                break

    def apply(self):
        """get config and apply to app.
        """
        recentlist = self.win.config["file.recent"]
        self.win.recent.setList(recentlist)

        for option, val in self.changedOptions.items():
            self.optionActions[option][0](val)
        self.changedOptions.clear()
        self.win.editor.settings.apply()

    def cancel(self):
        for option in self.changedOptions.keys():
            if option in self.win.config:
                self.optionActions[option][1](self.win.config[option])
            else:
                self.optionActions[option][1](
                    self.win.config.defaultOptions[option])
        self.changedOptions.clear()
        self.win.editor.settings.cancel()
Exemple #36
0
class PlayerControls(QWidget):
 
    play = pyqtSignal()
    pause = pyqtSignal()
    stop = pyqtSignal()
    next = pyqtSignal()
    previous = pyqtSignal()
    changeVolume = pyqtSignal(int)
    changeMuting = pyqtSignal(bool)
    changeRate = pyqtSignal(float)
 
    def __init__(self, parent=None):
        super(PlayerControls, self).__init__(parent)
 
        self.playerState = QMediaPlayer.StoppedState
        self.playerMuted = False
 
        self.playButton = QToolButton(clicked=self.playClicked)
        self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
 
        self.stopButton = QToolButton(clicked=self.stop)
        self.stopButton.setIcon(self.style().standardIcon(QStyle.SP_MediaStop))
        self.stopButton.setEnabled(False)
 
        self.nextButton = QToolButton(clicked=self.next)
        self.nextButton.setIcon(
                self.style().standardIcon(QStyle.SP_MediaSkipForward))
 
        self.previousButton = QToolButton(clicked=self.previous)
        self.previousButton.setIcon(
                self.style().standardIcon(QStyle.SP_MediaSkipBackward))
 
        self.muteButton = QToolButton(clicked=self.muteClicked)
        self.muteButton.setIcon(
                self.style().standardIcon(QStyle.SP_MediaVolume))
 
        self.volumeSlider = QSlider(Qt.Horizontal,
                sliderMoved=self.changeVolume)
        self.volumeSlider.setRange(0, 100)
 
        self.rateBox = QComboBox(activated=self.updateRate)
        self.rateBox.addItem("0.5x", 0.5)
        self.rateBox.addItem("1.0x", 1.0)
        self.rateBox.addItem("2.0x", 2.0)
        self.rateBox.setCurrentIndex(1)
 
        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.stopButton)
        layout.addWidget(self.previousButton)
        layout.addWidget(self.playButton)
        layout.addWidget(self.nextButton)
        layout.addWidget(self.muteButton)
        layout.addWidget(self.volumeSlider)
        layout.addWidget(self.rateBox)
        self.setLayout(layout)
 
    def state(self):
        return self.playerState
 
    def setState(self,state):
        if state != self.playerState:
            self.playerState = state
 
            if state == QMediaPlayer.StoppedState:
                self.stopButton.setEnabled(False)
                self.playButton.setIcon(
                        self.style().standardIcon(QStyle.SP_MediaPlay))

            elif state == QMediaPlayer.PlayingState:
                self.stopButton.setEnabled(True)
                self.playButton.setIcon(
                        self.style().standardIcon(QStyle.SP_MediaPause))
            elif state == QMediaPlayer.PausedState:
                self.stopButton.setEnabled(True)
                self.playButton.setIcon(
                        self.style().standardIcon(QStyle.SP_MediaPlay))
 
    def volume(self):
        return self.volumeSlider.value()
 
    def setVolume(self, volume):
        self.volumeSlider.setValue(volume)
 
    def isMuted(self):
        return self.playerMuted
 
    def setMuted(self, muted):
        if muted != self.playerMuted:
            self.playerMuted = muted
 
            self.muteButton.setIcon(
                    self.style().standardIcon(
                            QStyle.SP_MediaVolumeMuted if muted else QStyle.SP_MediaVolume))
 
    def playClicked(self):
        if self.playerState in (QMediaPlayer.StoppedState, QMediaPlayer.PausedState):
            self.play.emit()
        elif self.playerState == QMediaPlayer.PlayingState:
            self.pause.emit()
 
    def muteClicked(self):
        self.changeMuting.emit(not self.playerMuted)
 
    def playbackRate(self):
        return self.rateBox.itemData(self.rateBox.currentIndex())
 
    def setPlaybackRate(self, rate):
        for i in range(self.rateBox.count()):
            if qFuzzyCompare(rate, self.rateBox.itemData(i)):
                self.rateBox.setCurrentIndex(i)
                return
 
        self.rateBox.addItem("%dx" % rate, rate)
        self.rateBox.setCurrentIndex(self.rateBox.count() - 1)
 
    def updateRate(self):
        self.changeRate.emit(self.playbackRate())
Exemple #37
0
class Property(QWidget):
    def _title(self, t):
        l = QLabel(t, self)
        l.setStyleSheet("font-weight: bold")
        return l

    def __init__(self, parent: typing.Optional['QWidget']):
        super().__init__(parent=parent)
        self.scrollView = QScrollArea(self)
        self.scrollView.setWidgetResizable(True)
        self.scrollView.setHorizontalScrollBarPolicy(
            QtCore.Qt.ScrollBarPolicy.ScrollBarAlwaysOff)

        self.textAttr = QWidget(self)
        self.textAttr.setSizePolicy(QSizePolicy.Policy.Preferred,
                                    QSizePolicy.Policy.Preferred)
        self.textAttrBox = QVBoxLayout()
        self.textAttr.setLayout(self.textAttrBox)

        self.cascades = SvgBar(self)
        self.cascades.setVisible(False)
        self.cascades.onDelete = self.deleteCascade
        self.cascades.onDrag = self.resortCascade
        self.cascades.onCopy = self.onCopy
        self.textAttrBox.addWidget(self.cascades)

        self.svgId = QLabel('N/A', self)
        self.textAttrBox.addWidget(self._title(TR('Type')))
        self.textAttrBox.addWidget(self.svgId)

        self.startXOffsetLabel = self._title(TR('Overlay: Start X Percentage'))
        self.textAttrBox.addWidget(self.startXOffsetLabel)
        self.startXOffset = QSlider(self)
        self.startXOffset.setOrientation(QtCore.Qt.Orientation.Horizontal)
        self.startXOffset.setMinimum(0)
        self.startXOffset.setMaximum(3)
        self.startXOffset.valueChanged.connect(
            lambda e: self.offsetChanged('xo',
                                         self.startXOffset.value() * 0.25))
        self.textAttrBox.addWidget(self.startXOffset)

        self.textAttrBox.addWidget(self._title(TR('Text')))
        self.text = QTextEdit(self)
        self.text.textChanged.connect(self.textChanged)
        self.text.installEventFilter(self)
        self.textAttrBox.addWidget(self.text)

        self.textFont = QComboBox(self)
        fontFamilies = QFontDatabase()
        for s in fontFamilies.families():
            self.textFont.addItem(s)
        self.textFont.currentIndexChanged.connect(self.fontChanged)
        self.textFont.setEditable(True)

        self.textSize = QComboBox(self)
        for i in range(8, 150, 1):
            if i <= 32:
                self.textSize.addItem(str(i))
            elif i <= 80 and i % 2 == 0:
                self.textSize.addItem(str(i))
            elif i % 10 == 0:
                self.textSize.addItem(str(i))
        self.textSize.currentIndexChanged.connect(self.sizeChanged)
        self.textSize.setEditable(True)
        self._addBiBoxInTextAttrBox(self._title(TR("Font Family")),
                                    self.textFont,
                                    self._title(TR("Font Size")),
                                    self.textSize)

        self.textAlign = QComboBox(self)
        self.textPlace = QComboBox(self)
        for c in [self.textAlign, self.textPlace]:
            c.addItem(TR('Center'), 'c')
            c.addItem(TR('Top'), 't')
            c.addItem(TR('Bottom'), 'b')
            c.addItem(TR('Left'), 'l')
            c.addItem(TR('Right'), 'r')
        self.textAlign.currentIndexChanged.connect(self.alignChanged)
        self.textPlace.currentIndexChanged.connect(self.placeChanged)
        self._addBiBoxInTextAttrBox(self._title(TR("Alignment")),
                                    self.textAlign,
                                    self._title(TR("Placement")),
                                    self.textPlace)

        self.textX = QSpinBox(self)
        self.textY = QSpinBox(self)
        for c in [self.textX, self.textY]:
            c.setValue(0)
            c.setMinimum(-1e5)
            c.setMaximum(1e5)
        self.textX.valueChanged.connect(lambda e: self.offsetChanged('x', e))
        self.textY.valueChanged.connect(lambda e: self.offsetChanged('y', e))
        self._addBiBoxInTextAttrBox(self._title(TR("Offset X")), self.textX,
                                    self._title(TR("Offset Y")), self.textY)

        # self.setLayout(self.textAttrBox)
        self.scrollView.setWidget(self.textAttr)
        box = QVBoxLayout()
        box.addWidget(self.scrollView)
        box.setContentsMargins(0, 0, 0, 0)
        self.setLayout(box)

        self.show()
        self.setSizePolicy(QSizePolicy.Policy.MinimumExpanding,
                           QSizePolicy.Policy.MinimumExpanding)

        self.updating = False

    def _genVBox(p, w1, w2, m=0):
        placeAttr = QWidget(p)
        placeAttrBox = QVBoxLayout()
        placeAttrBox.setContentsMargins(m, m, m, m)
        placeAttr.setLayout(placeAttrBox)
        placeAttrBox.addWidget(w1)
        placeAttrBox.addWidget(w2)
        return placeAttr

    def eventFilter(self, a0, a1: QtCore.QEvent) -> bool:
        if a1.type() == QtCore.QEvent.FocusIn and a0 is self.text:
            self.findMainWin().mapview.data.begin()
            self.oldTextStore = []
            for x in self.selection():
                self.oldTextStore.append(x.dup())
        if a1.type() == QtCore.QEvent.FocusOut and a0 is self.text:
            for x in self.selection():
                self._getMapData()._appendHistory(self.oldTextStore.pop(0), x)
            self.oldTextStore.clear()
        return super().eventFilter(a0, a1)

    def _addBiBoxInTextAttrBox(self, t1, w1, t2, w2):
        placeAttr = QWidget(self)
        placeAttrBox = QHBoxLayout()
        placeAttrBox.setContentsMargins(0, 0, 0, 0)
        placeAttr.setLayout(placeAttrBox)
        placeAttrBox.addWidget(Property._genVBox(self, t1, w1))
        placeAttrBox.addWidget(Property._genVBox(self, t2, w2))
        self.textAttrBox.addWidget(placeAttr)

    def textChanged(self):
        for x in self.selection():
            x.text = self.text.toPlainText()
        self.findMainWin().mapview.pan(0, 0)

    def _getMapData(self) -> MapData:
        return self.findMainWin().mapview.data

    def onCopy(self, src):
        if not src:
            return
        v = json.dumps([MapDataElement(src).todict()])
        QApplication.clipboard().setText(v)
        self.findMainWin().mapview.setFocus()

    def deleteCascade(self, idx: int):
        self._getMapData().begin()
        item = self.selection()[0]
        old = item.pack()
        if idx == 0:
            item.src = item.cascades[0]
            item.cascades = item.cascades[1:]
        else:
            item.cascades = item.cascades[:idx - 1] + item.cascades[idx - 1 +
                                                                    1:]
        self._getMapData()._appendHistoryPacked(old, item.pack())
        self.findMainWin().mapview.setFocus()
        self.update()

    def resortCascade(self, fr: int, to: int):
        self._getMapData().begin()
        item: MapDataElement = self.selection()[0]
        last = item.pack()
        if to == 0:
            fr = fr - 1
            old = item.src
            item.src = item.cascades[fr]
            item.cascades = [old] + item.cascades[:fr] + item.cascades[fr + 1:]
        elif fr == 0:
            old = item.src
            item.src = item.cascades[0]
            item.cascades = item.cascades[1:to] + [old] + item.cascades[to:]
        else:
            fr, to = fr - 1, to - 1
            fold = item.cascades[fr]
            item.cascades = item.cascades[:fr] + item.cascades[fr + 1:]
            item.cascades = item.cascades[:to] + [fold] + item.cascades[to:]
        self._getMapData()._appendHistoryPacked(last, item.pack())
        self.findMainWin().mapview.setFocus()
        self.update()

    def _foreach(self, f):
        mainData = self.findMainWin().mapview.data
        mainData.begin()
        for x in self.selection():
            old = x.dup()
            f(x)
            # print(old.pack(), x.pack())
            mainData._appendHistory(old, x)
        self.findMainWin().mapview.pan(0, 0)

    def fontChanged(self, e):
        self._foreach(lambda x: x.set("textFont", self.textFont.itemText(e)))

    def offsetChanged(self, t, v):
        if t == 'x' or t == 'y':
            self._foreach(lambda x: x.set(t == 'x' and 'textX' or 'textY', v))
        else:  # xo
            self.startXOffsetLabel.setText(
                TR('Overlay: Start X Percentage') + ' ' + str(int(v * 100)) +
                "%")
            self._foreach(lambda x: x.set("startXOffset", v))

    def sizeChanged(self, e):
        self._foreach(
            lambda x: x.set("textSize", int(self.textSize.itemText(e))))

    def alignChanged(self, e):
        self._foreach(lambda x: x.set("textAlign", self.textAlign.itemData(e)))

    def placeChanged(self, e):
        self._foreach(
            lambda x: x.set("textPlacement", self.textPlace.itemData(e)))

    def findMainWin(self):
        p = self.parent()
        while not isinstance(p, QMainWindow):
            p = p.parent()
        return p

    def selection(self):
        if self.updating:
            return []
        return list(
            map(lambda l: l.data,
                self.findMainWin().mapview.selector.labels))

    def update(self):
        data = self.findMainWin().mapview.data
        self.findMainWin().barHistory.setText('{}@{}'.format(
            data.historyCap, len(data.history)))
        self.findMainWin().barHistory.setStyleSheet(
            data.historyCap and "background: #80f0f000" or "")

        def toggle(v):
            for k in self.__dict__:  # disbale all widgets
                w = self.__dict__[k]
                if isinstance(w, QWidget) and w != self.scrollView:
                    w.setEnabled(v)

        toggle(False)
        self.cascades.setVisible(False)

        items = self.selection()
        if not items:
            self.svgId.setText("N/A")
            return
        toggle(True)

        e = items[-1]
        self.updating = True
        self.svgId.setText(e.src.svgId)
        if e.cascades and len(items) == 1:
            self.cascades.update([(
                e.src.svgId, SvgSource.Search.fullpath(e.src.svgId)
            )] + list(
                map(lambda x: (x.svgId, SvgSource.Search.fullpath(x.svgId)),
                    e.cascades)))
            self.cascades.setVisible(True)
        self.text.setText(e.text)
        self.textFont.setEditText(e.textFont)
        self.textSize.setEditText(str(e.textSize))
        self._setAP(self.textAlign, e.textAlign)
        self._setAP(self.textPlace, e.textPlacement)
        self.textX.setValue(e.textX)
        self.textY.setValue(e.textY)
        self.startXOffset.setValue(int(e.startXOffset / 0.25))
        self.updating = False

    def _setAP(self, w: QComboBox, d):
        for i in range(0, w.count()):
            if w.itemData(i) == d:
                w.setCurrentIndex(i)
                break
Exemple #38
0
class Settings_Net(QWidget):
    def __init__(self, parent: QWidget):
        super(Settings_Net, self).__init__(parent)
        self._layout = QVBoxLayout()
        self.setLayout(self._layout)
        # construct layout
        min_width = 150
        self.user_agents = dict()
        self.user_agents['chrome_win7_x64'] = (
            'Chrome 41, Windows 7 x64', 'Mozilla/5.0 (Windows NT 6.1; WOW64) '
            'AppleWebKit/537.36 (KHTML, like Gecko) '
            'Chrome/41.0.2227.0 Safari/537.36')
        self.user_agents['chrome_linux_64'] = (
            'Chrome 41, Linux x86_64', 'Mozilla/5.0 (X11; Linux x86_64) '
            'AppleWebKit/537.36 (KHTML, like Gecko) '
            'Chrome/41.0.2227.0 Safari/537.36')
        self.user_agents['chrome_android'] = (
            'Chrome 47, Android 4.3 Galaxy-S3',
            'Mozilla/5.0 (Linux; Android 4.3; GT-I9300 Build/JSS15J) '
            'AppleWebKit/537.36 (KHTML, like Gecko) '
            'Chrome/47.0.2526.83 Mobile Safari/537.36')
        self.user_agents['firefox_win32'] = (
            'Firefox 40, Windows 7 32-bit',
            'Mozilla/5.0 (Windows NT 6.1; rv:40.0) '
            'Gecko/20100101 Firefox/40.1')
        self.user_agents['firefox_android'] = (
            'Firefox, Android 4.3 Galaxy-S3',
            'Mozilla/5.0 (Android 4.3; Mobile; rv:43.0) '
            'Gecko/43.0 Firefox/43.0')
        self.user_agents['edge_win10'] = (
            'Microsoft Edge, Windows 10 x64',
            'Mozilla/5.0 (Windows NT 10.0; Win64; x64) '
            'AppleWebKit/537.36 (KHTML, like Gecko) '
            'Chrome/42.0.2311.135 Safari/537.36 Edge/12.246')
        # server URL
        self._l_surl = QHBoxLayout()
        self._l_ua = QHBoxLayout()
        self._lbl_surl = QLabel(self.tr('Server URL:'), self)
        self._lbl_surl.setMinimumWidth(min_width)
        self._le_surl = QLineEdit(self)
        self._l_surl.addWidget(self._lbl_surl)
        self._l_surl.addWidget(self._le_surl)
        self._layout.addLayout(self._l_surl)
        # emulate browser combo box
        self._l_eb = QHBoxLayout()
        self._lbl_eb = QLabel(self.tr('Emulate browser:'), self)
        self._lbl_eb.setMinimumWidth(min_width)
        self._cb_eb = QComboBox(self)
        self._cb_eb.setEditable(False)
        self._cb_eb.setInsertPolicy(QComboBox.InsertAtBottom)
        ua_keylist = [i for i in self.user_agents.keys()]
        ua_keylist.sort()
        for key_id in ua_keylist:
            b_tuple = self.user_agents[key_id]
            display_string = b_tuple[0]
            self._cb_eb.addItem(display_string, QVariant(str(key_id)))
        self._cb_eb.addItem(self.tr('<Custom>'), QVariant('custom'))
        self._l_eb.addWidget(self._lbl_eb)
        self._l_eb.addWidget(self._cb_eb)
        self._layout.addLayout(self._l_eb)
        # custom user-agent string
        self._lbl_ua = QLabel(self.tr('User-agent string:'), self)
        self._lbl_ua.setMinimumWidth(min_width)
        self._le_ua = QLineEdit(self)
        self._l_ua.addWidget(self._lbl_ua)
        self._l_ua.addWidget(self._le_ua)
        self._layout.addLayout(self._l_ua)
        # proxy settings
        self._l_proxy = QHBoxLayout()
        self._lbl_proxy = QLabel(self.tr('Proxy type:'), self)
        self._lbl_proxy.setMinimumWidth(min_width)
        self._cb_proxy = QComboBox(self)
        self._cb_proxy.setEditable(False)
        self._cb_proxy.addItem(self.tr('No proxy'), QVariant('none'))
        self._cb_proxy.addItem(self.tr('HTTP proxy'), QVariant('http'))
        self._cb_proxy.addItem(self.tr('SOCKS5 proxy'), QVariant('socks5'))
        self._l_proxy.addWidget(self._lbl_proxy)
        self._l_proxy.addWidget(self._cb_proxy)
        self._layout.addLayout(self._l_proxy)
        self._l_proxy_s = QHBoxLayout()
        self._lbl_proxy_s = QLabel(self.tr('Proxy addr:port:'), self)
        self._lbl_proxy_s.setMinimumWidth(min_width)
        self._le_proxy_addr = QLineEdit(self)
        self._l_proxy_s.addWidget(self._lbl_proxy_s)
        self._l_proxy_s.addWidget(self._le_proxy_addr)
        self._layout.addLayout(self._l_proxy_s)
        # all connections
        self._cb_eb.currentIndexChanged.connect(
            self.on_cb_eb_current_index_changed)
        self._cb_proxy.currentIndexChanged.connect(
            self.on_cb_proxy_current_index_changed)
        # finalize
        self._layout.addStretch()

    @pyqtSlot(int)
    def on_cb_eb_current_index_changed(self, index: int):
        key_id = str(self._cb_eb.currentData(Qt.UserRole))
        if key_id == 'custom':
            self._le_ua.setEnabled(True)
            return
        self._le_ua.setEnabled(False)
        if key_id in self.user_agents:
            b_tuple = self.user_agents[key_id]
            ua_str = b_tuple[1]
            self._le_ua.setText(ua_str)

    @pyqtSlot(int)
    def on_cb_proxy_current_index_changed(self, index: int):
        if index == 0:
            self._le_proxy_addr.setEnabled(False)
        else:
            self._le_proxy_addr.setEnabled(True)

    def ua_select(self, key_id: str):
        cnt = self._cb_eb.count()
        for i in range(cnt):
            item_key_id = str(self._cb_eb.itemData(i, Qt.UserRole))
            if item_key_id == key_id:
                self._cb_eb.setCurrentIndex(i)
                break

    def load_from_config(self, cfg: configparser.ConfigParser):
        # defaults
        xnova_url = 'uni4.xnova.su'
        user_agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0'
        user_agent_id = 'custom'
        proxy = ''
        if 'net' in cfg:
            xnova_url = cfg['net']['xnova_url']
            user_agent = cfg['net']['user_agent']
            user_agent_id = cfg['net']['user_agent_id']
            proxy = cfg['net']['proxy']
        self._le_surl.setText(xnova_url)
        self._le_surl.setEnabled(
            False)  # cannot be edited by user, for safety!
        # deal with user-agent
        self._le_ua.setText(user_agent)
        if user_agent_id == 'custom':
            self._le_ua.setEnabled(True)
        else:
            self._le_ua.setEnabled(False)
        self.ua_select(user_agent_id)
        # deal with proxy
        if proxy == '':
            self._le_proxy_addr.setText('')
            self._cb_proxy.setCurrentIndex(0)
            self._le_proxy_addr.setEnabled(False)
        elif proxy.startswith('http://'):
            self._cb_proxy.setCurrentIndex(1)
            proxy_addr = proxy[7:]
            self._le_proxy_addr.setText(proxy_addr)
            self._le_proxy_addr.setEnabled(True)
        elif proxy.startswith('socks5://'):
            self._cb_proxy.setCurrentIndex(2)
            proxy_addr = proxy[9:]
            self._le_proxy_addr.setText(proxy_addr)
            self._le_proxy_addr.setEnabled(True)
        else:
            raise ValueError('Invalid proxy setting: ' + proxy)

    def save_to_config(self, cfg: configparser.ConfigParser):
        # ensure there is a 'net' section
        if 'net' not in cfg:
            cfg.add_section('net')
        # skip server url
        # deal with user-agent
        user_agent_id = ''
        user_agent = ''
        idx = self._cb_eb.currentIndex()
        if idx >= 0:
            user_agent_id = str(self._cb_eb.itemData(idx, Qt.UserRole))
            cfg['net']['user_agent_id'] = user_agent_id
        user_agent = self._le_ua.text().strip()
        if user_agent != '':
            cfg['net']['user_agent'] = user_agent
        # deal with proxy
        idx = self._cb_proxy.currentIndex()
        proxy_addr = self._le_proxy_addr.text().strip()
        if idx == 0:
            cfg['net']['proxy'] = ''
        elif idx == 1:
            cfg['net']['proxy'] = 'http://' + proxy_addr
        elif idx == 2:
            cfg['net']['proxy'] = 'socks5://' + proxy_addr
        logger.debug('Saved network config')
Exemple #39
0
 def _setAP(self, w: QComboBox, d):
     for i in range(0, w.count()):
         if w.itemData(i) == d:
             w.setCurrentIndex(i)
             break
Exemple #40
0
class PianoWidget(QWidget):
    noteOn = pyqtSignal(music.Pitch, int)
    noteOff = pyqtSignal(music.Pitch)

    def __init__(self, parent, app):
        super().__init__(parent)

        self._app = app

        self.setFocusPolicy(Qt.StrongFocus)

        layout = QVBoxLayout()
        self.setLayout(layout)

        toolbar = QHBoxLayout()
        layout.addLayout(toolbar)

        self.focus_indicator = QLed(self)
        self.focus_indicator.setMinimumSize(24, 24)
        self.focus_indicator.setMaximumSize(24, 24)
        toolbar.addWidget(self.focus_indicator)

        toolbar.addSpacing(10)

        self._keyboard_listener = None

        self.keyboard_selector = QComboBox()
        self.keyboard_selector.addItem("None", userData=None)
        for device_id, port_info in self._app.midi_hub.list_devices():
            self.keyboard_selector.addItem(
                "%s (%s)" % (port_info.name, port_info.client_info.name),
                userData=device_id)
        self.keyboard_selector.currentIndexChanged.connect(
            self.onKeyboardDeviceChanged)
        toolbar.addWidget(self.keyboard_selector)

        toolbar.addSpacing(10)

        # speaker icon should go here...
        #tb = QToolButton(self)
        #tb.setIcon(QIcon.fromTheme('multimedia-volume-control'))
        #toolbar.addWidget(tb)

        self.volume = QSlider(Qt.Horizontal, self)
        self.volume.setMinimumWidth(200)
        self.volume.setMinimum(0)
        self.volume.setMaximum(127)
        self.volume.setValue(127)
        self.volume.setTickPosition(QSlider.TicksBothSides)
        toolbar.addWidget(self.volume)

        toolbar.addStretch(1)

        self.piano_keys = PianoKeys(self)
        layout.addWidget(self.piano_keys)

    def close(self):
        if not super().close():  # pragma: no coverage
            return False

        if self._keyboard_listener is not None:
            self._keyboard_listener.remove()
            self._keyboard_listener = None

        return True

    def onKeyboardDeviceChanged(self, index):
        if self._keyboard_listener is not None:
            self._keyboard_listener.remove()
            self._keyboard_listener = None

        device_id = self.keyboard_selector.itemData(index)
        if device_id is not None:
            self._keyboard_listener = self._app.midi_hub.listeners.add(
                device_id, self.midiEvent)

    def focusInEvent(self, event):
        event.accept()
        self.focus_indicator.setValue(True)

    def focusOutEvent(self, event):
        event.accept()
        self.focus_indicator.setValue(False)

    def keyPressEvent(self, event):
        self.piano_keys.keyPressEvent(event)

    def keyReleaseEvent(self, event):
        self.piano_keys.keyReleaseEvent(event)

    def midiEvent(self, event):
        self.piano_keys.midiEvent(event)
Exemple #41
0
class BarometerV2(COMCUPluginBase):
    def __init__(self, *args):
        super().__init__(BrickletBarometerV2, *args)

        self.barometer = self.device

        self.cbe_air_pressure = CallbackEmulator(self.barometer.get_air_pressure,
                                                 None,
                                                 self.cb_air_pressure,
                                                 self.increase_error_count)

        self.cbe_altitude = CallbackEmulator(self.barometer.get_altitude,
                                             None,
                                             self.cb_altitude,
                                             self.increase_error_count)

        self.cbe_temperature = CallbackEmulator(self.barometer.get_temperature,
                                                None,
                                                self.cb_temperature,
                                                self.increase_error_count)

        self.current_altitude = CurveValueWrapper()
        self.current_air_pressure = CurveValueWrapper()

        self.calibration = None
        self.btn_clear_graphs = QPushButton('Clear Graphs')
        self.btn_calibration = QPushButton('Calibration...')
        self.btn_calibration.clicked.connect(self.btn_calibration_clicked)

        self.lbl_temperature_value = QLabel('-')

        self.sbox_reference_air_pressure = QDoubleSpinBox()
        self.sbox_reference_air_pressure.setMinimum(260)
        self.sbox_reference_air_pressure.setMaximum(1260)
        self.sbox_reference_air_pressure.setDecimals(3)
        self.sbox_reference_air_pressure.setValue(1013.25)
        self.sbox_reference_air_pressure.setSingleStep(1)
        self.btn_use_current = QPushButton('Use Current')
        self.btn_use_current.clicked.connect(self.btn_use_current_clicked)
        self.sbox_reference_air_pressure.editingFinished.connect(self.sbox_reference_air_pressure_editing_finished)

        self.sbox_moving_avg_len_air_pressure = QSpinBox()
        self.sbox_moving_avg_len_air_pressure.setMinimum(1)
        self.sbox_moving_avg_len_air_pressure.setMaximum(1000)
        self.sbox_moving_avg_len_air_pressure.setSingleStep(1)
        self.sbox_moving_avg_len_air_pressure.setValue(100)
        self.sbox_moving_avg_len_air_pressure.editingFinished.connect(self.sbox_moving_avg_len_editing_finished)

        self.sbox_moving_avg_len_temperature = QSpinBox()
        self.sbox_moving_avg_len_temperature.setMinimum(1)
        self.sbox_moving_avg_len_temperature.setMaximum(1000)
        self.sbox_moving_avg_len_temperature.setSingleStep(1)
        self.sbox_moving_avg_len_temperature.setValue(100)
        self.sbox_moving_avg_len_temperature.editingFinished.connect(self.sbox_moving_avg_len_editing_finished)

        plot_config_air_pressure = [('Air Pressure',
                                     Qt.red,
                                     self.current_air_pressure,
                                     '{:.3f} hPa (QFE)'.format)]

        plot_config_altitude = [('Altitude',
                                 Qt.darkGreen,
                                 self.current_altitude,
                                 lambda value: '{:.3f} m ({:.3f} ft)'.format(value, value / METER_TO_FEET_DIVISOR))]

        self.plot_widget_air_pressure = PlotWidget('Air Pressure [hPa]',
                                                   plot_config_air_pressure,
                                                   self.btn_clear_graphs,
                                                   y_resolution=0.001)

        self.plot_widget_altitude = PlotWidget('Altitude [m]',
                                               plot_config_altitude,
                                               self.btn_clear_graphs,
                                               y_resolution=0.001)

        self.combo_data_rate = QComboBox()
        self.combo_data_rate.addItem('Off', BrickletBarometerV2.DATA_RATE_OFF)
        self.combo_data_rate.addItem('1 Hz', BrickletBarometerV2.DATA_RATE_1HZ)
        self.combo_data_rate.addItem('10 Hz', BrickletBarometerV2.DATA_RATE_10HZ)
        self.combo_data_rate.addItem('25 Hz', BrickletBarometerV2.DATA_RATE_25HZ)
        self.combo_data_rate.addItem('50 Hz', BrickletBarometerV2.DATA_RATE_50HZ)
        self.combo_data_rate.addItem('75 Hz', BrickletBarometerV2.DATA_RATE_75HZ)
        self.combo_data_rate.currentIndexChanged.connect(self.new_sensor_config)

        self.combo_air_pressure_low_pass_filter = QComboBox()
        self.combo_air_pressure_low_pass_filter.addItem('Off', BrickletBarometerV2.LOW_PASS_FILTER_OFF)
        self.combo_air_pressure_low_pass_filter.addItem('1/9th', BrickletBarometerV2.LOW_PASS_FILTER_1_9TH)
        self.combo_air_pressure_low_pass_filter.addItem('1/20th', BrickletBarometerV2.LOW_PASS_FILTER_1_20TH)
        self.combo_air_pressure_low_pass_filter.currentIndexChanged.connect(self.new_sensor_config)

        # Layout
        layout_h1 = QHBoxLayout()
        layout_h1.addWidget(self.plot_widget_air_pressure)
        layout_h1.addWidget(self.plot_widget_altitude)

        layout = QVBoxLayout(self)
        layout.addLayout(layout_h1)

        line = QFrame()
        line.setObjectName("line")
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)

        layout.addWidget(line)

        layout_h2 = QHBoxLayout()
        layout_h2.addWidget(QLabel('Reference Air Pressure [hPa]:'))
        layout_h2.addWidget(self.sbox_reference_air_pressure)
        layout_h2.addWidget(self.btn_use_current)
        layout_h2.addStretch()
        layout_h2.addWidget(QLabel('Temperature:'))
        layout_h2.addWidget(self.lbl_temperature_value)
        layout_h2.addStretch()
        layout_h2.addWidget(self.btn_clear_graphs)

        layout.addLayout(layout_h2)

        layout_h3 = QHBoxLayout()
        layout_h3.addWidget(QLabel('Air Pressure Moving Average Length:'))
        layout_h3.addWidget(self.sbox_moving_avg_len_air_pressure)
        layout_h3.addStretch()
        layout_h3.addWidget(QLabel('Temperature Moving Average Length:'))
        layout_h3.addWidget(self.sbox_moving_avg_len_temperature)

        layout.addLayout(layout_h3)

        layout_h4 = QHBoxLayout()
        layout_h4.addWidget(QLabel('Data Rate:'))
        layout_h4.addWidget(self.combo_data_rate)
        layout_h4.addStretch()
        layout_h4.addWidget(QLabel('Air Pressure Low Pass Filter:'))
        layout_h4.addWidget(self.combo_air_pressure_low_pass_filter)
        layout_h4.addStretch()
        layout_h4.addWidget(self.btn_calibration)

        layout.addLayout(layout_h4)

    def start(self):
        async_call(self.barometer.get_reference_air_pressure, None, self.get_reference_air_pressure_async, self.increase_error_count)
        async_call(self.barometer.get_moving_average_configuration, None, self.get_moving_average_configuration_async, self.increase_error_count)
        async_call(self.barometer.get_sensor_configuration, None, self.get_sensor_configuration_async, self.increase_error_count)

        self.cbe_air_pressure.set_period(50)
        self.cbe_altitude.set_period(50)
        self.cbe_temperature.set_period(100)

        self.plot_widget_air_pressure.stop = False
        self.plot_widget_altitude.stop = False

    def stop(self):
        self.cbe_air_pressure.set_period(0)
        self.cbe_altitude.set_period(0)
        self.cbe_temperature.set_period(0)

        self.plot_widget_air_pressure.stop = True
        self.plot_widget_altitude.stop = True

    def destroy(self):
        if self.calibration != None:
            self.calibration.close()

    @staticmethod
    def has_device_identifier(device_identifier):
        return device_identifier == BrickletBarometerV2.DEVICE_IDENTIFIER

    def cb_air_pressure(self, air_pressure):
        self.current_air_pressure.value = air_pressure / 1000.0

    def cb_altitude(self, altitude):
        self.current_altitude.value = altitude / 1000.0

    def cb_temperature(self, temperature):
        self.lbl_temperature_value.setText('{:.2f} °C'.format(temperature / 100.0))

    def get_reference_air_pressure_async(self, air_pressure):
        self.sbox_reference_air_pressure.setValue(air_pressure / 1000.0)

    def btn_use_current_clicked(self):
        self.barometer.set_reference_air_pressure(0)
        async_call(self.barometer.get_reference_air_pressure, None, self.get_reference_air_pressure_async, self.increase_error_count)

    def sbox_reference_air_pressure_editing_finished(self):
        self.barometer.set_reference_air_pressure(self.sbox_reference_air_pressure.value() * 1000.0)

    def get_moving_average_configuration_async(self, avg):
        m_avg_air_pressure, m_avg_temperature = avg

        self.sbox_moving_avg_len_air_pressure.setValue(m_avg_air_pressure)
        self.sbox_moving_avg_len_temperature.setValue(m_avg_temperature)

    def sbox_moving_avg_len_editing_finished(self):
        self.barometer.set_moving_average_configuration(self.sbox_moving_avg_len_air_pressure.value(),
                                                        self.sbox_moving_avg_len_temperature.value())

    def get_sensor_configuration_async(self, config):
        data_rate, air_pressure_low_pass_filter = config

        self.combo_data_rate.setCurrentIndex(self.combo_data_rate.findData(data_rate))
        self.combo_air_pressure_low_pass_filter.setCurrentIndex(self.combo_air_pressure_low_pass_filter.findData(air_pressure_low_pass_filter))

    def new_sensor_config(self):
        data_rate = self.combo_data_rate.itemData(self.combo_data_rate.currentIndex())
        air_pressure_low_pass_filter = self.combo_air_pressure_low_pass_filter.itemData(self.combo_air_pressure_low_pass_filter.currentIndex())

        self.barometer.set_sensor_configuration(data_rate, air_pressure_low_pass_filter)

    def btn_calibration_clicked(self):
        if self.calibration == None:
            self.calibration = Calibration(self)

        self.btn_calibration.setEnabled(False)
        self.calibration.show()
Exemple #42
0
class CSVOptionsWindow(QWidget):
    def __init__(self, mainwindow):
        QWidget.__init__(self, mainwindow, Qt.Window)
        self._setupUi()
        self.doc = mainwindow.doc
        self.model = mainwindow.model.csv_options
        self.tableModel = CSVOptionsTableModel(self.model, self.tableView)
        self.model.view = self
        self.encodingComboBox.addItems(SUPPORTED_ENCODINGS)

        self.cancelButton.clicked.connect(self.hide)
        self.continueButton.clicked.connect(self.model.continue_import)
        self.targetComboBox.currentIndexChanged.connect(self.targetIndexChanged)
        self.layoutComboBox.currentIndexChanged.connect(self.layoutIndexChanged)
        self.rescanButton.clicked.connect(self.rescanClicked)

    def _setupUi(self):
        self.setWindowTitle(tr("CSV Options"))
        self.resize(526, 369)
        self.verticalLayout = QVBoxLayout(self)
        msg = tr(
            "Specify which CSV columns correspond to which transaction fields. You must also "
            "uncheck the \"Import\" column for lines that don\'t represent a transaction "
            "(header, footer, comments)."
        )
        self.label = QLabel(msg)
        self.label.setWordWrap(True)
        self.verticalLayout.addWidget(self.label)
        self.gridLayout = QGridLayout()
        self.label_2 = QLabel(tr("Layout:"))
        self.gridLayout.addWidget(self.label_2, 0, 0, 1, 1)
        self.layoutComboBox = QComboBox(self)
        self.layoutComboBox.setMinimumSize(QtCore.QSize(160, 0))
        self.gridLayout.addWidget(self.layoutComboBox, 0, 1, 1, 1)
        self.label_4 = QLabel(tr("Delimiter:"))
        self.gridLayout.addWidget(self.label_4, 0, 3, 1, 1)
        self.fieldSeparatorEdit = QLineEdit(self)
        self.fieldSeparatorEdit.setMaximumSize(QtCore.QSize(30, 16777215))
        self.gridLayout.addWidget(self.fieldSeparatorEdit, 0, 4, 1, 1)
        self.targetComboBox = QComboBox(self)
        self.gridLayout.addWidget(self.targetComboBox, 1, 1, 1, 1)
        self.label_3 = QLabel(tr("Target:"))
        self.gridLayout.addWidget(self.label_3, 1, 0, 1, 1)
        self.encodingComboBox = QComboBox(self)
        self.gridLayout.addWidget(self.encodingComboBox, 1, 4, 1, 1)
        spacerItem = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.gridLayout.addItem(spacerItem, 2, 2, 1, 1)
        self.horizontalLayout_2 = QHBoxLayout()
        self.horizontalLayout_2.setSpacing(0)
        spacerItem1 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.horizontalLayout_2.addItem(spacerItem1)
        self.rescanButton = QPushButton(tr("Rescan"))
        sizePolicy = QSizePolicy(QSizePolicy.Maximum, QSizePolicy.Fixed)
        sizePolicy.setHorizontalStretch(0)
        sizePolicy.setVerticalStretch(0)
        sizePolicy.setHeightForWidth(self.rescanButton.sizePolicy().hasHeightForWidth())
        self.rescanButton.setSizePolicy(sizePolicy)
        self.horizontalLayout_2.addWidget(self.rescanButton)
        self.gridLayout.addLayout(self.horizontalLayout_2, 2, 3, 1, 2)
        self.label_5 = QLabel(tr("Encoding:"))
        self.gridLayout.addWidget(self.label_5, 1, 3, 1, 1)
        self.verticalLayout.addLayout(self.gridLayout)
        self.tableView = QTableView(self)
        self.tableView.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.tableView.setSelectionBehavior(QAbstractItemView.SelectRows)
        self.tableView.setShowGrid(False)
        self.tableView.horizontalHeader().setHighlightSections(False)
        self.tableView.verticalHeader().setVisible(False)
        self.tableView.verticalHeader().setDefaultSectionSize(18)
        self.verticalLayout.addWidget(self.tableView)
        self.horizontalLayout = QHBoxLayout()
        spacerItem2 = QSpacerItem(40, 20, QSizePolicy.Expanding, QSizePolicy.Minimum)
        self.horizontalLayout.addItem(spacerItem2)
        self.cancelButton = QPushButton(tr("Cancel"))
        self.cancelButton.setShortcut("Esc")
        self.horizontalLayout.addWidget(self.cancelButton)
        self.continueButton = QPushButton(tr("Continue Import"))
        self.continueButton.setDefault(True)
        self.horizontalLayout.addWidget(self.continueButton)
        self.verticalLayout.addLayout(self.horizontalLayout)

    # --- Private
    def _newLayout(self):
        title = tr("New Layout")
        msg = tr("Choose a name for your new layout:")
        name, ok = QInputDialog.getText(self, title, msg)
        if ok and name:
            self.model.new_layout(name)

    def _renameLayout(self):
        title = tr("Rename Layout")
        msg = tr("Choose a name for your layout:")
        name, ok = QInputDialog.getText(self, title, msg)
        if ok and name:
            self.model.rename_selected_layout(name)

    # --- Event Handling
    def layoutIndexChanged(self, index):
        # This one is a little complicated. We want to only be able to select the layouts. If
        # anything else is clicked, we revert back to the old index. If the item has user data,
        # it means that an action has to be performed.
        if index < 0:
            return
        elif index < len(self.model.layout_names):
            layout_name = None if index == 0 else str(self.layoutComboBox.itemText(index))
            self.model.select_layout(layout_name)
        else:
            self.layoutComboBox.setCurrentIndex(self.layoutComboBox.findText(self.model.layout.name))
            data = str(self.layoutComboBox.itemData(index))
            if data == NEW_LAYOUT:
                self._newLayout()
            elif data == RENAME_LAYOUT:
                self._renameLayout()
            elif data == DELETE_LAYOUT:
                self.model.delete_selected_layout()

    def rescanClicked(self):
        self.model.encoding_index = self.encodingComboBox.currentIndex()
        self.model.field_separator = str(self.fieldSeparatorEdit.text())
        self.model.rescan()

    def targetIndexChanged(self, index):
        self.model.selected_target_index = index

    # --- model --> view
    # hide() is called from the model, but is already covered by QWidget
    def refresh_columns(self):
        self.tableModel.beginResetModel()
        self.tableModel.endResetModel()

    def refresh_columns_name(self):
        self.tableModel.refreshColumnsName()

    def refresh_layout_menu(self):
        self.layoutComboBox.currentIndexChanged.disconnect(self.layoutIndexChanged)
        self.layoutComboBox.clear()
        self.layoutComboBox.addItems(self.model.layout_names)
        self.layoutComboBox.insertSeparator(self.layoutComboBox.count())
        self.layoutComboBox.addItem(tr("New Layout..."), NEW_LAYOUT)
        self.layoutComboBox.addItem(tr("Rename Selected Layout..."), RENAME_LAYOUT)
        self.layoutComboBox.addItem(tr("Delete Selected Layout"), DELETE_LAYOUT)
        self.layoutComboBox.setCurrentIndex(self.layoutComboBox.findText(self.model.layout.name))
        self.layoutComboBox.currentIndexChanged.connect(self.layoutIndexChanged)

    def refresh_lines(self):
        self.tableModel.beginResetModel()
        self.tableModel.endResetModel()
        self.fieldSeparatorEdit.setText(self.model.field_separator)

    def refresh_targets(self):
        self.targetComboBox.currentIndexChanged.disconnect(self.targetIndexChanged)
        self.targetComboBox.clear()
        self.targetComboBox.addItems(self.model.target_account_names)
        self.targetComboBox.currentIndexChanged.connect(self.targetIndexChanged)

    def show(self):
        # For non-modal dialogs, show() is not enough to bring the window at the forefront, we have
        # to call raise() as well
        QWidget.show(self)
        self.raise_()

    def show_message(self, msg):
        title = "Warning"
        QMessageBox.warning(self, title, msg)
Exemple #43
0
class AddDialog(QDialog):
    worker = None
    selected_show = None
    results = []

    def __init__(self, parent, worker, current_status, default=None):
        QDialog.__init__(self, parent)
        self.resize(950, 700)
        self.setWindowTitle('Search/Add from Remote')
        self.worker = worker
        self.current_status = current_status
        self.default = default
        if default:
            self.setWindowTitle('Search/Add from Remote for new show: %s' %
                                default)

        # Get available search methods and default to keyword search if not reported by the API
        search_methods = self.worker.engine.mediainfo.get(
            'search_methods', [utils.SEARCH_METHOD_KW])

        layout = QVBoxLayout()

        # Create top layout
        top_layout = QHBoxLayout()

        if utils.SEARCH_METHOD_KW in search_methods:
            self.search_rad = QRadioButton('By keyword:')
            self.search_rad.setChecked(True)
            self.search_txt = QLineEdit()
            self.search_txt.setClearButtonEnabled(True)
            self.search_txt.returnPressed.connect(self.s_search)
            if default:
                self.search_txt.setText(default)
            self.search_btn = QPushButton('Search')
            self.search_btn.clicked.connect(self.s_search)
            top_layout.addWidget(self.search_rad)
            top_layout.addWidget(self.search_txt)
        else:
            top_layout.setAlignment(QtCore.Qt.AlignRight)

        top_layout.addWidget(self.search_btn)

        # Create filter line
        filters_layout = QHBoxLayout()

        if utils.SEARCH_METHOD_SEASON in search_methods:
            self.season_rad = QRadioButton('By season:')
            self.season_combo = QComboBox()
            self.season_combo.addItem('Winter', utils.SEASON_WINTER)
            self.season_combo.addItem('Spring', utils.SEASON_SPRING)
            self.season_combo.addItem('Summer', utils.SEASON_SUMMER)
            self.season_combo.addItem('Fall', utils.SEASON_FALL)

            self.season_year = QSpinBox()

            today = date.today()
            current_season = (today.month - 1) / 3

            self.season_year.setRange(1900, today.year)
            self.season_year.setValue(today.year)
            self.season_combo.setCurrentIndex(current_season)

            filters_layout.addWidget(self.season_rad)
            filters_layout.addWidget(self.season_combo)
            filters_layout.addWidget(self.season_year)

            filters_layout.setAlignment(QtCore.Qt.AlignLeft)
            filters_layout.addWidget(QSplitter())
        else:
            filters_layout.setAlignment(QtCore.Qt.AlignRight)

        view_combo = QComboBox()
        view_combo.addItem('Card view')
        view_combo.addItem('Table view')
        view_combo.currentIndexChanged.connect(self.s_change_view)

        filters_layout.addWidget(view_combo)

        # Create central content
        self.contents = QStackedWidget()

        # Set up views
        tableview = AddTableDetailsView(None, self.worker)
        tableview.changed.connect(self.s_selected)

        cardview = AddCardView(api_info=self.worker.engine.api_info)
        cardview.changed.connect(self.s_selected)
        cardview.doubleClicked.connect(self.s_show_details)

        self.contents.addWidget(cardview)
        self.contents.addWidget(tableview)

        # Use for testing
        #self.set_results([{'id': 1, 'title': 'Hola', 'image': 'https://omaera.org/icon.png'}])

        bottom_buttons = QDialogButtonBox()
        bottom_buttons.addButton("Cancel", QDialogButtonBox.RejectRole)
        self.add_btn = bottom_buttons.addButton("Add",
                                                QDialogButtonBox.AcceptRole)
        self.add_btn.setEnabled(False)
        bottom_buttons.accepted.connect(self.s_add)
        bottom_buttons.rejected.connect(self.close)

        # Finish layout
        layout.addLayout(top_layout)
        layout.addLayout(filters_layout)
        layout.addWidget(self.contents)
        layout.addWidget(bottom_buttons)
        self.setLayout(layout)

        if utils.SEARCH_METHOD_SEASON in search_methods:
            self.search_txt.setFocus()

    def worker_call(self, function, ret_function, *args, **kwargs):
        # Run worker in a thread
        self.worker.set_function(function, ret_function, *args, **kwargs)
        self.worker.start()

    def _enable_widgets(self, enable):
        self.search_btn.setEnabled(enable)
        self.contents.currentWidget().setEnabled(enable)

    def set_results(self, results):
        self.results = results
        self.contents.currentWidget().setResults(self.results)

    # Slots
    def s_show_details(self):
        detailswindow = DetailsDialog(self, self.worker, self.selected_show)
        detailswindow.setModal(True)
        detailswindow.show()

    def s_change_view(self, item):
        self.contents.currentWidget().getModel().setResults(None)
        self.contents.setCurrentIndex(item)
        self.contents.currentWidget().getModel().setResults(self.results)

    def s_search(self):
        if self.search_rad.isChecked():
            criteria = self.search_txt.text().strip()
            if not criteria:
                return
            method = utils.SEARCH_METHOD_KW
        elif self.season_rad.isChecked():
            criteria = (self.season_combo.itemData(
                self.season_combo.currentIndex()), self.season_year.value())
            method = utils.SEARCH_METHOD_SEASON

        self.contents.currentWidget().clearSelection()
        self.selected_show = None

        self._enable_widgets(False)
        self.add_btn.setEnabled(False)

        self.worker_call('search', self.r_searched, criteria, method)

    def s_selected(self, show):
        self.selected_show = show
        self.add_btn.setEnabled(True)

    def s_add(self):
        if self.selected_show:
            self.worker_call('add_show', self.r_added, self.selected_show,
                             self.current_status)

    # Worker responses
    def r_searched(self, result):
        self._enable_widgets(True)

        if result['success']:
            self.set_results(result['result'])
            """
            if self.table.currentRow() is 0:  # Row number hasn't changed but the data probably has!
                self.s_show_selected(self.table.item(0, 0))
            self.table.setCurrentItem(self.table.item(0, 0))"""
        else:
            self.set_results(None)

    def r_added(self, result):
        if result['success']:
            if self.default:
                self.accept()
            else:
                QMessageBox.information(self, 'Information',
                                        "Show added successfully")
Exemple #44
0
class BarometerV2(COMCUPluginBase):
    def __init__(self, *args):
        super().__init__(BrickletBarometerV2, *args)

        self.barometer = self.device

        self.cbe_air_pressure = CallbackEmulator(self.barometer.get_air_pressure,
                                                 None,
                                                 self.cb_air_pressure,
                                                 self.increase_error_count)

        self.cbe_altitude = CallbackEmulator(self.barometer.get_altitude,
                                             None,
                                             self.cb_altitude,
                                             self.increase_error_count)

        self.cbe_temperature = CallbackEmulator(self.barometer.get_temperature,
                                                None,
                                                self.cb_temperature,
                                                self.increase_error_count)

        self.current_altitude = CurveValueWrapper()
        self.current_air_pressure = CurveValueWrapper()

        self.calibration = None
        self.btn_clear_graphs = QPushButton('Clear Graphs')
        self.btn_calibration = QPushButton('Calibration...')
        self.btn_calibration.clicked.connect(self.btn_calibration_clicked)

        self.lbl_temperature_value = QLabel('-')

        self.sbox_reference_air_pressure = QDoubleSpinBox()
        self.sbox_reference_air_pressure.setMinimum(260)
        self.sbox_reference_air_pressure.setMaximum(1260)
        self.sbox_reference_air_pressure.setDecimals(3)
        self.sbox_reference_air_pressure.setValue(1013.25)
        self.sbox_reference_air_pressure.setSingleStep(1)
        self.btn_use_current = QPushButton('Use Current')
        self.btn_use_current.clicked.connect(self.btn_use_current_clicked)
        self.sbox_reference_air_pressure.editingFinished.connect(self.sbox_reference_air_pressure_editing_finished)

        self.sbox_moving_avg_len_air_pressure = QSpinBox()
        self.sbox_moving_avg_len_air_pressure.setMinimum(1)
        self.sbox_moving_avg_len_air_pressure.setMaximum(1000)
        self.sbox_moving_avg_len_air_pressure.setSingleStep(1)
        self.sbox_moving_avg_len_air_pressure.setValue(100)
        self.sbox_moving_avg_len_air_pressure.editingFinished.connect(self.sbox_moving_avg_len_editing_finished)

        self.sbox_moving_avg_len_temperature = QSpinBox()
        self.sbox_moving_avg_len_temperature.setMinimum(1)
        self.sbox_moving_avg_len_temperature.setMaximum(1000)
        self.sbox_moving_avg_len_temperature.setSingleStep(1)
        self.sbox_moving_avg_len_temperature.setValue(100)
        self.sbox_moving_avg_len_temperature.editingFinished.connect(self.sbox_moving_avg_len_editing_finished)

        plot_config_air_pressure = [('Air Pressure',
                                     Qt.red,
                                     self.current_air_pressure,
                                     '{:.3f} mbar (QFE)'.format)]

        plot_config_altitude = [('Altitude',
                                 Qt.darkGreen,
                                 self.current_altitude,
                                 lambda value: '{:.3f} m ({:.3f} ft)'.format(value, value / METER_TO_FEET_DIVISOR))]

        self.plot_widget_air_pressure = PlotWidget('Air Pressure [mbar]',
                                                   plot_config_air_pressure,
                                                   self.btn_clear_graphs,
                                                   y_resolution=0.001)

        self.plot_widget_altitude = PlotWidget('Altitude [m]',
                                               plot_config_altitude,
                                               self.btn_clear_graphs,
                                               y_resolution=0.001)

        self.combo_data_rate = QComboBox()
        self.combo_data_rate.addItem('Off', BrickletBarometerV2.DATA_RATE_OFF)
        self.combo_data_rate.addItem('1 Hz', BrickletBarometerV2.DATA_RATE_1HZ)
        self.combo_data_rate.addItem('10 Hz', BrickletBarometerV2.DATA_RATE_10HZ)
        self.combo_data_rate.addItem('25 Hz', BrickletBarometerV2.DATA_RATE_25HZ)
        self.combo_data_rate.addItem('50 Hz', BrickletBarometerV2.DATA_RATE_50HZ)
        self.combo_data_rate.addItem('75 Hz', BrickletBarometerV2.DATA_RATE_75HZ)
        self.combo_data_rate.currentIndexChanged.connect(self.new_sensor_config)

        self.combo_air_pressure_low_pass_filter = QComboBox()
        self.combo_air_pressure_low_pass_filter.addItem('Off', BrickletBarometerV2.LOW_PASS_FILTER_OFF)
        self.combo_air_pressure_low_pass_filter.addItem('1/9th', BrickletBarometerV2.LOW_PASS_FILTER_1_9TH)
        self.combo_air_pressure_low_pass_filter.addItem('1/20th', BrickletBarometerV2.LOW_PASS_FILTER_1_20TH)
        self.combo_air_pressure_low_pass_filter.currentIndexChanged.connect(self.new_sensor_config)

        # Layout
        layout_h1 = QHBoxLayout()
        layout_h1.addWidget(self.plot_widget_air_pressure)
        layout_h1.addWidget(self.plot_widget_altitude)

        layout = QVBoxLayout(self)
        layout.addLayout(layout_h1)

        line = QFrame()
        line.setObjectName("line")
        line.setFrameShape(QFrame.HLine)
        line.setFrameShadow(QFrame.Sunken)

        layout.addWidget(line)

        layout_h2 = QHBoxLayout()
        layout_h2.addWidget(QLabel('Reference Air Pressure [mbar]:'))
        layout_h2.addWidget(self.sbox_reference_air_pressure)
        layout_h2.addWidget(self.btn_use_current)
        layout_h2.addStretch()
        layout_h2.addWidget(QLabel('Temperature:'))
        layout_h2.addWidget(self.lbl_temperature_value)
        layout_h2.addStretch()
        layout_h2.addWidget(self.btn_clear_graphs)

        layout.addLayout(layout_h2)

        layout_h3 = QHBoxLayout()
        layout_h3.addWidget(QLabel('Air Pressure Moving Average Length:'))
        layout_h3.addWidget(self.sbox_moving_avg_len_air_pressure)
        layout_h3.addStretch()
        layout_h3.addWidget(QLabel('Temperature Moving Average Length:'))
        layout_h3.addWidget(self.sbox_moving_avg_len_temperature)

        layout.addLayout(layout_h3)

        layout_h4 = QHBoxLayout()
        layout_h4.addWidget(QLabel('Data Rate:'))
        layout_h4.addWidget(self.combo_data_rate)
        layout_h4.addStretch()
        layout_h4.addWidget(QLabel('Air Pressure Low Pass Filter:'))
        layout_h4.addWidget(self.combo_air_pressure_low_pass_filter)
        layout_h4.addStretch()
        layout_h4.addWidget(self.btn_calibration)

        layout.addLayout(layout_h4)

    def start(self):
        async_call(self.barometer.get_reference_air_pressure, None, self.get_reference_air_pressure_async, self.increase_error_count)
        async_call(self.barometer.get_moving_average_configuration, None, self.get_moving_average_configuration_async, self.increase_error_count)
        async_call(self.barometer.get_sensor_configuration, None, self.get_sensor_configuration_async, self.increase_error_count)

        self.cbe_air_pressure.set_period(50)
        self.cbe_altitude.set_period(50)
        self.cbe_temperature.set_period(100)

        self.plot_widget_air_pressure.stop = False
        self.plot_widget_altitude.stop = False

    def stop(self):
        self.cbe_air_pressure.set_period(0)
        self.cbe_altitude.set_period(0)
        self.cbe_temperature.set_period(0)

        self.plot_widget_air_pressure.stop = True
        self.plot_widget_altitude.stop = True

    def destroy(self):
        if self.calibration != None:
            self.calibration.close()

    @staticmethod
    def has_device_identifier(device_identifier):
        return device_identifier == BrickletBarometerV2.DEVICE_IDENTIFIER

    def cb_air_pressure(self, air_pressure):
        self.current_air_pressure.value = air_pressure / 1000.0

    def cb_altitude(self, altitude):
        self.current_altitude.value = altitude / 1000.0

    def cb_temperature(self, temperature):
        self.lbl_temperature_value.setText('{:.2f} °C'.format(temperature / 100.0))

    def get_reference_air_pressure_async(self, air_pressure):
        self.sbox_reference_air_pressure.setValue(air_pressure / 1000.0)

    def btn_use_current_clicked(self):
        self.barometer.set_reference_air_pressure(0)
        async_call(self.barometer.get_reference_air_pressure, None, self.get_reference_air_pressure_async, self.increase_error_count)

    def sbox_reference_air_pressure_editing_finished(self):
        self.barometer.set_reference_air_pressure(self.sbox_reference_air_pressure.value() * 1000.0)

    def get_moving_average_configuration_async(self, avg):
        m_avg_air_pressure, m_avg_temperature = avg

        self.sbox_moving_avg_len_air_pressure.setValue(m_avg_air_pressure)
        self.sbox_moving_avg_len_temperature.setValue(m_avg_temperature)

    def sbox_moving_avg_len_editing_finished(self):
        self.barometer.set_moving_average_configuration(self.sbox_moving_avg_len_air_pressure.value(),
                                                        self.sbox_moving_avg_len_temperature.value())

    def get_sensor_configuration_async(self, config):
        data_rate, air_pressure_low_pass_filter = config

        self.combo_data_rate.setCurrentIndex(self.combo_data_rate.findData(data_rate))
        self.combo_air_pressure_low_pass_filter.setCurrentIndex(self.combo_air_pressure_low_pass_filter.findData(air_pressure_low_pass_filter))

    def new_sensor_config(self):
        data_rate = self.combo_data_rate.itemData(self.combo_data_rate.currentIndex())
        air_pressure_low_pass_filter = self.combo_air_pressure_low_pass_filter.itemData(self.combo_air_pressure_low_pass_filter.currentIndex())

        self.barometer.set_sensor_configuration(data_rate, air_pressure_low_pass_filter)

    def btn_calibration_clicked(self):
        if self.calibration == None:
            self.calibration = Calibration(self)

        self.btn_calibration.setEnabled(False)
        self.calibration.show()
class ChartLab(QWidget):
    def __init__(self, datahub_entry: DataHubEntry,
                 factor_center: FactorCenter):
        super(ChartLab, self).__init__()

        # ---------------- ext var ----------------

        self.__data_hub = datahub_entry
        self.__factor_center = factor_center
        self.__data_center = self.__data_hub.get_data_center(
        ) if self.__data_hub is not None else None
        self.__data_utility = self.__data_hub.get_data_utility(
        ) if self.__data_hub is not None else None

        self.__inited = False
        self.__plot_table = {}
        self.__paint_data = None

        # ------------- plot resource -------------

        self.__figure = plt.figure()
        self.__canvas = FigureCanvas(self.__figure)

        # -------------- ui resource --------------

        self.__data_frame_widget = None

        self.__combo_factor = QComboBox()
        self.__label_comments = QLabel('')

        # Parallel comparison
        self.__radio_parallel_comparison = QRadioButton('横向比较')
        self.__combo_year = QComboBox()
        self.__combo_quarter = QComboBox()
        self.__combo_industry = QComboBox()

        # Longitudinal comparison
        self.__radio_longitudinal_comparison = QRadioButton('纵向比较')
        self.__combo_stock = SecuritiesSelector(self.__data_utility)

        # Limitation
        self.__line_lower = QLineEdit('')
        self.__line_upper = QLineEdit('')

        self.__button_draw = QPushButton('绘图')
        self.__button_show = QPushButton('数据')

        self.init_ui()

    # ---------------------------------------------------- UI Init -----------------------------------------------------

    def init_ui(self):
        self.__layout_control()
        self.__config_control()

    def __layout_control(self):
        main_layout = QVBoxLayout()
        self.setLayout(main_layout)
        self.setMinimumSize(1280, 800)

        bottom_layout = QHBoxLayout()
        main_layout.addWidget(self.__canvas, 99)
        main_layout.addLayout(bottom_layout, 1)

        group_box, group_layout = create_v_group_box('因子')
        bottom_layout.addWidget(group_box, 2)

        group_layout.addWidget(self.__combo_factor)
        group_layout.addWidget(self.__label_comments)

        group_box, group_layout = create_v_group_box('比较方式')
        bottom_layout.addWidget(group_box, 2)

        line = QHBoxLayout()
        line.addWidget(self.__radio_parallel_comparison, 1)
        line.addWidget(self.__combo_industry, 5)
        line.addWidget(self.__combo_year, 5)
        line.addWidget(self.__combo_quarter, 5)
        group_layout.addLayout(line)

        line = QHBoxLayout()
        line.addWidget(self.__radio_longitudinal_comparison, 1)
        line.addWidget(self.__combo_stock, 10)
        group_layout.addLayout(line)

        group_box, group_layout = create_v_group_box('范围限制')
        bottom_layout.addWidget(group_box, 1)

        line = QHBoxLayout()
        line.addWidget(QLabel('下限'))
        line.addWidget(self.__line_lower)
        group_layout.addLayout(line)

        line = QHBoxLayout()
        line.addWidget(QLabel('上限'))
        line.addWidget(self.__line_upper)
        group_layout.addLayout(line)

        col = QVBoxLayout()
        col.addWidget(self.__button_draw)
        col.addWidget(self.__button_show)
        bottom_layout.addLayout(col, 1)

    def __config_control(self):
        for year in range(now().year, 1989, -1):
            self.__combo_year.addItem(str(year), str(year))
        self.__combo_year.setCurrentIndex(1)

        self.__combo_quarter.addItem('一季报', '03-31')
        self.__combo_quarter.addItem('中报', '06-30')
        self.__combo_quarter.addItem('三季报', '09-30')
        self.__combo_quarter.addItem('年报', '12-31')
        self.__combo_quarter.setCurrentIndex(3)

        self.__combo_industry.addItem('全部', '全部')
        identities = self.__data_utility.get_all_industries()
        for identity in identities:
            self.__combo_industry.addItem(identity, identity)

        if self.__factor_center is not None:
            factors = self.__factor_center.get_all_factors()
            for fct in factors:
                self.__combo_factor.addItem(fct, fct)
        self.on_factor_updated(0)

        self.__combo_stock.setEnabled(False)
        self.__radio_parallel_comparison.setChecked(True)

        self.__radio_parallel_comparison.setToolTip(TIP_PARALLEL_COMPARISON)
        self.__radio_longitudinal_comparison.setToolTip(
            TIP_LONGITUDINAL_COMPARISON)
        self.__line_lower.setToolTip(TIP_LIMIT_UPPER_LOWER)
        self.__line_upper.setToolTip(TIP_LIMIT_UPPER_LOWER)
        self.__button_show.setToolTip(TIP_BUTTON_SHOW)

        self.__button_draw.clicked.connect(self.on_button_draw)
        self.__button_show.clicked.connect(self.on_button_show)

        self.__combo_factor.currentIndexChanged.connect(self.on_factor_updated)
        self.__radio_parallel_comparison.clicked.connect(
            self.on_radio_comparison)
        self.__radio_longitudinal_comparison.clicked.connect(
            self.on_radio_comparison)

        mpl.rcParams['font.sans-serif'] = ['Microsoft YaHei']
        mpl.rcParams['axes.unicode_minus'] = False

    def on_factor_updated(self, value):
        self.__line_lower.setText('')
        self.__line_upper.setText('')
        factor = self.__combo_factor.itemData(value)
        comments = self.__factor_center.get_factor_comments(factor)
        self.__label_comments.setText(comments)

    def on_button_draw(self):
        factor = self.__combo_factor.currentData()
        lower = str2float_safe(self.__line_lower.text(), None)
        upper = str2float_safe(self.__line_upper.text(), None)

        if self.__radio_parallel_comparison.isChecked():
            year = self.__combo_year.currentData()
            month_day = self.__combo_quarter.currentData()
            period = year + '-' + month_day
            industry = self.__combo_industry.currentData()
            self.plot_factor_parallel_comparison(factor, industry,
                                                 text_auto_time(period), lower,
                                                 upper)
        else:
            securities = self.__combo_stock.get_input_securities()
            self.plot_factor_longitudinal_comparison(factor, securities)

    def on_button_show(self):
        if self.__data_frame_widget is not None and \
                self.__data_frame_widget.isVisible():
            self.__data_frame_widget.close()
        if self.__paint_data is not None:
            self.__data_frame_widget = DataFrameWidget(self.__paint_data)
            self.__data_frame_widget.show()

    def on_radio_comparison(self):
        if self.__radio_parallel_comparison.isChecked():
            self.__combo_year.setEnabled(True)
            self.__combo_quarter.setEnabled(True)
            self.__line_lower.setEnabled(True)
            self.__line_upper.setEnabled(True)
            self.__combo_stock.setEnabled(False)
        else:
            self.__combo_year.setEnabled(False)
            self.__combo_quarter.setEnabled(False)
            self.__line_lower.setEnabled(False)
            self.__line_upper.setEnabled(False)
            self.__combo_stock.setEnabled(True)

    # ---------------------------------------------------------------------------------------

    def plot_factor_parallel_comparison(self, factor: str, industry: str,
                                        period: datetime.datetime,
                                        lower: float, upper: float):
        identities = ''
        if industry != '全部':
            identities = self.__data_utility.get_industry_stocks(industry)
        df = self.__data_center.query_from_factor('Factor.Finance',
                                                  identities, (period, period),
                                                  fields=[factor],
                                                  readable=True)

        s1 = df[factor]
        if lower is not None and upper is not None:
            s1 = s1.apply(lambda x: (x if x < upper else upper)
                          if x > lower else lower)
        elif lower is not None:
            s1 = s1.apply(lambda x: x if x > lower else lower)
        elif upper is not None:
            s1 = s1.apply(lambda x: x if x < upper else upper)

        plt.clf()
        plt.subplot(1, 1, 1)
        s1.hist(bins=100)
        plt.title(factor)

        self.__canvas.draw()
        self.__canvas.flush_events()

        self.__paint_data = df
        self.__paint_data.sort_values(factor, inplace=True)

    def plot_factor_longitudinal_comparison(self, factor: str,
                                            securities: str):
        df = self.__data_center.query_from_factor('Factor.Finance',
                                                  securities,
                                                  None,
                                                  fields=[factor],
                                                  readable=True)
        # Only for annual report
        df = df[df['period'].dt.month == 12]
        df['报告期'] = df['period']
        df.set_index('报告期', inplace=True)

        s1 = df[factor]

        plt.clf()
        plt.subplot(1, 1, 1)
        s1.plot.line()
        plt.title(factor)

        self.__canvas.draw()
        self.__canvas.flush_events()

        self.__paint_data = df
        self.__paint_data.sort_values('period', ascending=False, inplace=True)

    # ---------------------------------------------------------------------------------------

    def plot(self):
        self.plot_histogram_statistics()

    def plot_histogram_statistics(self):
        # --------------------------- The Data and Period We Want to Check ---------------------------

        stock = ''
        period = (text_auto_time('2018-12-01'), text_auto_time('2018-12-31'))

        # --------------------------------------- Query Pattern --------------------------------------

        # fields_balance_sheet = ['货币资金', '资产总计', '负债合计',
        #                         '短期借款', '一年内到期的非流动负债', '其他流动负债',
        #                         '长期借款', '应付债券', '其他非流动负债', '流动负债合计',
        #                         '应收票据', '应收账款', '其他应收款', '预付款项',
        #                         '交易性金融资产', '可供出售金融资产',
        #                         '在建工程', '商誉', '固定资产']
        # fields_income_statement = ['营业收入', '营业总收入', '减:营业成本', '息税前利润']
        #
        # df, result = batch_query_readable_annual_report_pattern(
        #     self.__data_hub, stock, period, fields_balance_sheet, fields_income_statement)
        # if result is not None:
        #     return result

        # df_balance_sheet, result = query_readable_annual_report_pattern(
        #     self.__data_hub, 'Finance.BalanceSheet', stock, period, fields_balance_sheet)
        # if result is not None:
        #     print('Data Error')
        #
        # df_income_statement, result = query_readable_annual_report_pattern(
        #     self.__data_hub, 'Finance.IncomeStatement', stock, period, fields_income_statement)
        # if result is not None:
        #     print('Data Error')

        # -------------------------------- Merge and Pre-processing --------------------------------

        # df = pd.merge(df_balance_sheet,
        #               df_income_statement,
        #               how='left', on=['stock_identity', 'period'])

        # df = df.sort_values('period')
        # df = df.reset_index()
        # df = df.fillna(0)
        # df = df.replace(0, 1)

        # ------------------------------------- Calc and Plot -------------------------------------

        mpl.rcParams['font.sans-serif'] = ['Microsoft YaHei']
        mpl.rcParams['axes.unicode_minus'] = False

        # font = matplotlib.font_manager.FontProperties(fname='C:/Windows/Fonts/msyh.ttf')
        # mpl.rcParams['axes.unicode_minus'] = False

        # df['应收款'] = df['应收账款'] + df['应收票据']
        # df['净资产'] = df['资产总计'] - df['负债合计']
        # df['短期负债'] = df['短期借款'] + df['一年内到期的非流动负债'] + df['其他流动负债']
        # df['有息负债'] = df['短期负债'] + df['长期借款'] + df['应付债券'] + df['其他非流动负债']
        # df['金融资产'] = df['交易性金融资产'] + df['可供出售金融资产']
        #
        # df['财务费用正'] = df['减:财务费用'].apply(lambda x: x if x > 0 else 0)
        # df['三费'] = df['减:销售费用'] + df['减:管理费用'] + df['财务费用正']

        df = self.__data_utility.auto_query(
            '', period,
            ['减:财务费用', '减:销售费用', '减:管理费用', '营业总收入', '营业收入', '减:营业成本'],
            ['stock_identity', 'period'])

        df['毛利润'] = df['营业收入'] - df['减:营业成本']
        df['财务费用正'] = df['减:财务费用'].apply(lambda x: x if x > 0 else 0)
        df['三费'] = df['减:销售费用'] + df['减:管理费用'] + df['财务费用正']

        s1 = df['三费'] / df['营业总收入']
        s1 = s1.apply(lambda x: (x if x < 1 else 1) if x > -0.1 else -0.1)
        plt.subplot(2, 1, 1)
        s1.hist(bins=100)
        plt.title('三费/营业总收入')

        s2 = df['三费'] / df['毛利润']
        s2 = s2.apply(lambda x: (x if x < 1 else 1) if x > -0.1 else -0.1)
        plt.subplot(2, 1, 2)
        s2.hist(bins=100)
        plt.title('三费/毛利润')

        # s1 = df['货币资金'] / df['有息负债']
        # s1 = s1.apply(lambda x: x if x < 10 else 10)
        # plt.subplot(2, 1, 1)
        # s1.hist(bins=100)
        # plt.title('货币资金/有息负债')
        #
        # s2 = df['有息负债'] / df['资产总计']
        # plt.subplot(2, 1, 2)
        # s2.hist(bins=100)
        # plt.title('有息负债/资产总计')

        # s1 = df['应收款'] / df['营业收入']
        # s1 = s1.apply(lambda x: x if x < 2 else 2)
        # plt.subplot(4, 1, 1)
        # s1.hist(bins=100)
        # plt.title('应收款/营业收入')
        #
        # s2 = df['其他应收款'] / df['营业收入']
        # s2 = s2.apply(lambda x: x if x < 1 else 1)
        # plt.subplot(4, 1, 2)
        # s2.hist(bins=100)
        # plt.title('其他应收款/营业收入')
        #
        # s3 = df['预付款项'] / df['营业收入']
        # s3 = s3.apply(lambda x: x if x < 1 else 1)
        # plt.subplot(4, 1, 3)
        # s3.hist(bins=100)
        # plt.title('预付款项/营业收入')
        #
        # s4 = df['预付款项'] / df['减:营业成本']
        # s4 = s4.apply(lambda x: x if x < 1 else 1)
        # plt.subplot(4, 1, 4)
        # s4.hist(bins=100)
        # plt.title('预付款项/营业成本')

        # s1 = df['商誉'] / df['净资产']
        # s1 = s1.apply(lambda x: (x if x < 1 else 1) if x > 0 else 0)
        # plt.subplot(3, 1, 1)
        # s1.hist(bins=100)
        # plt.title('商誉/净资产')
        #
        # s2 = df['在建工程'] / df['净资产']
        # s2 = s2.apply(lambda x: (x if x < 1 else 1) if x > 0 else 0)
        # plt.subplot(3, 1, 2)
        # s2.hist(bins=100)
        # plt.title('在建工程/净资产')
        #
        # s2 = df['在建工程'] / df['资产总计']
        # s2 = s2.apply(lambda x: (x if x < 1 else 1) if x > 0 else 0)
        # plt.subplot(3, 1, 3)
        # s2.hist(bins=100)
        # plt.title('在建工程/资产总计')

        # s1 = df['固定资产'] / df['资产总计']
        # s1 = s1.apply(lambda x: (x if x < 1 else 1) if x > 0 else 0)
        # plt.subplot(2, 1, 1)
        # s1.hist(bins=100)
        # plt.title('固定资产/资产总计')
        #
        # s2 = df['息税前利润'] / df['固定资产']
        # s2 = s2.apply(lambda x: (x if x < 10 else 10) if x > -10 else -10)
        # plt.subplot(2, 1, 2)
        # s2.hist(bins=100)
        # plt.title('息税前利润/固定资产')

        # self.plot_proportion([
        #     ChartLab.PlotItem('固定资产', '资产总计', 0, 1),
        #     ChartLab.PlotItem('息税前利润', '固定资产', -10, 10),
        # ], text_auto_time('2018-12-31'))

        self.repaint()

    class PlotItem:
        def __init__(self,
                     num: str,
                     den: str,
                     lower: float or None = None,
                     upper: float or None = None,
                     bins: int = 100):
            self.numerator = num
            self.denominator = den
            self.limit_lower = lower
            self.limit_upper = upper
            self.plot_bins = bins

    def plot_proportion(self, plot_set: [PlotItem], period: datetime.datetime):
        df = self.prepare_plot_data(plot_set, period)

        plot_count = len(plot_set)
        for plot_index in range(plot_count):
            plot_item = plot_set[plot_index]
            s = df[plot_item.numerator] / df[plot_item.denominator]
            if plot_item.limit_lower is not None:
                s = s.apply(lambda x: max(x, plot_item.limit_lower))
            if plot_item.limit_upper is not None:
                s = s.apply(lambda x: min(x, plot_item.limit_upper))
            plt.subplot(plot_count, 1, plot_index + 1)
            s.hist(bins=plot_item.plot_bins)
            plt.title(plot_item.numerator + '/' + plot_item.denominator)

    def prepare_plot_data(self, plot_set: [PlotItem],
                          period: datetime.datetime) -> pd.DataFrame:
        fields = []
        for plot_item in plot_set:
            fields.append(plot_item.numerator)
            fields.append(plot_item.denominator)
        fields = list(set(fields))
        return self.__data_utility.auto_query(
            '', (period - datetime.timedelta(days=1), period), fields,
            ['stock_identity', 'period'])
Exemple #46
0
class AddDialog(QDialog):
    worker = None
    selected_show = None
    results = []

    def __init__(self, parent, worker, current_status, default=None):
        QDialog.__init__(self, parent)
        self.resize(950, 700)
        self.setWindowTitle('Search/Add from Remote')
        self.worker = worker
        self.current_status = current_status
        self.default = default
        if default:
            self.setWindowTitle('Search/Add from Remote for new show: %s' % default)
        
        # Get available search methods and default to keyword search if not reported by the API
        search_methods = self.worker.engine.mediainfo.get('search_methods', [utils.SEARCH_METHOD_KW])

        layout = QVBoxLayout()

        # Create top layout
        top_layout = QHBoxLayout()

        if utils.SEARCH_METHOD_KW in search_methods:
            self.search_rad = QRadioButton('By keyword:')
            self.search_rad.setChecked(True)
            self.search_txt = QLineEdit()
            self.search_txt.returnPressed.connect(self.s_search)
            if default:
                self.search_txt.setText(default)
            self.search_btn = QPushButton('Search')
            self.search_btn.clicked.connect(self.s_search)
            top_layout.addWidget(self.search_rad)
            top_layout.addWidget(self.search_txt)
        else:
            top_layout.setAlignment(QtCore.Qt.AlignRight)

        top_layout.addWidget(self.search_btn)
        
        # Create filter line
        filters_layout = QHBoxLayout()
        
        if utils.SEARCH_METHOD_SEASON in search_methods:
            self.season_rad = QRadioButton('By season:')
            self.season_combo = QComboBox()
            self.season_combo.addItem('Winter', utils.SEASON_WINTER)
            self.season_combo.addItem('Spring', utils.SEASON_SPRING)
            self.season_combo.addItem('Summer', utils.SEASON_SUMMER)
            self.season_combo.addItem('Fall', utils.SEASON_FALL)
        
            self.season_year = QSpinBox()

            today = date.today()
            current_season = (today.month - 1) / 3

            self.season_year.setRange(1900, today.year)
            self.season_year.setValue(today.year)
            self.season_combo.setCurrentIndex(current_season)

            filters_layout.addWidget(self.season_rad)
            filters_layout.addWidget(self.season_combo)
            filters_layout.addWidget(self.season_year)
        
            filters_layout.setAlignment(QtCore.Qt.AlignLeft)
            filters_layout.addWidget(QSplitter())
        else:
            filters_layout.setAlignment(QtCore.Qt.AlignRight)
        
        view_combo = QComboBox()
        view_combo.addItem('Table view')
        view_combo.addItem('Card view')
        view_combo.currentIndexChanged.connect(self.s_change_view)
        
        filters_layout.addWidget(view_combo)

        # Create central content
        self.contents = QStackedWidget()
        
        # Set up views
        tableview = AddTableDetailsView(None, self.worker)
        tableview.changed.connect(self.s_selected)
        self.contents.addWidget(tableview)
        
        cardview = AddCardView(api_info=self.worker.engine.api_info)
        cardview.changed.connect(self.s_selected)
        cardview.activated.connect(self.s_show_details)
        self.contents.addWidget(cardview)
        
        # Use for testing
        #self.set_results([{'id': 1, 'title': 'Hola', 'image': 'https://omaera.org/icon.png'}])

        bottom_buttons = QDialogButtonBox()
        bottom_buttons.addButton("Cancel", QDialogButtonBox.RejectRole)
        self.add_btn = bottom_buttons.addButton("Add", QDialogButtonBox.AcceptRole)
        self.add_btn.setEnabled(False)
        bottom_buttons.accepted.connect(self.s_add)
        bottom_buttons.rejected.connect(self.close)

        # Finish layout
        layout.addLayout(top_layout)
        layout.addLayout(filters_layout)
        layout.addWidget(self.contents)
        layout.addWidget(bottom_buttons)
        self.setLayout(layout)

        if utils.SEARCH_METHOD_SEASON in search_methods:
            self.search_txt.setFocus()

    def worker_call(self, function, ret_function, *args, **kwargs):
        # Run worker in a thread
        self.worker.set_function(function, ret_function, *args, **kwargs)
        self.worker.start()

    def _enable_widgets(self, enable):
        self.search_btn.setEnabled(enable)
        self.contents.currentWidget().setEnabled(enable)

    def set_results(self, results):
        self.results = results
        self.contents.currentWidget().setResults(self.results)

    # Slots
    def s_show_details(self):
        detailswindow = DetailsDialog(self, self.worker, self.selected_show)
        detailswindow.setModal(True)
        detailswindow.show()

    def s_change_view(self, item):
        self.contents.currentWidget().getModel().setResults(None)
        self.contents.setCurrentIndex(item)
        self.contents.currentWidget().getModel().setResults(self.results)
        
    def s_search(self):
        if self.search_rad.isChecked():
            criteria = self.search_txt.text().strip()
            if not criteria:
                return
            method = utils.SEARCH_METHOD_KW
        elif self.season_rad.isChecked():
            criteria = (self.season_combo.itemData(self.season_combo.currentIndex()), self.season_year.value())
            method = utils.SEARCH_METHOD_SEASON
        
        self.contents.currentWidget().clearSelection()
        self.selected_show = None
        
        self._enable_widgets(False)
        self.add_btn.setEnabled(False)
        
        self.worker_call('search', self.r_searched, criteria, method)
    
    def s_selected(self, show):
        self.selected_show = show
        self.add_btn.setEnabled(True)
        
    def s_add(self):
        if self.selected_show:
            self.worker_call('add_show', self.r_added, self.selected_show, self.current_status)

    # Worker responses
    def r_searched(self, result):
        self._enable_widgets(True)
        
        if result['success']:
            self.set_results(result['result'])
            
            """
            if self.table.currentRow() is 0:  # Row number hasn't changed but the data probably has!
                self.s_show_selected(self.table.item(0, 0))
            self.table.setCurrentItem(self.table.item(0, 0))"""
        else:
            self.set_results(None)

    def r_added(self, result):
        if result['success']:
            if self.default:
                self.accept()
class NumericParameterWidget(GenericParameterWidget):
    """Widget class for Numeric parameter."""
    def __init__(self, parameter, parent=None):
        """Constructor

        .. versionadded:: 2.2

        :param parameter: A NumericParameter object.
        :type parameter: NumericParameter

        """
        super().__init__(parameter, parent)

        self._input = QWidget()

        self._unit_widget = QLabel()
        self.set_unit()

        # Size policy
        self._spin_box_size_policy = QSizePolicy(
            QSizePolicy.Fixed, QSizePolicy.Fixed)

        label_policy = QSizePolicy(
            QSizePolicy.Minimum, QSizePolicy.Fixed)
        self._unit_widget.setSizePolicy(label_policy)

        # Add unit description to description
        description = self.help_label.text()
        if self._parameter.allowed_units:
            description += '<br><br>Available units:'
            description += '<ul>'
            for allowed_unit in self._parameter.allowed_units:
                description += '<li>'
                description += '<b>' + allowed_unit.name + '</b>: '
                description += allowed_unit.description or ''
                description += '</li>'
            description += '</ul>'
        self.help_label.setText(description)

    def get_parameter(self):
        """Obtain numeric parameter object from the current widget state.

        :returns: A NumericParameter from the current state of widget

        """
        self._parameter.value = self._input.value()
        if len(self._parameter.allowed_units) > 1:
            current_index = self._unit_widget.currentIndex()
            unit = self._unit_widget.itemData(current_index, Qt.UserRole)
            if hasattr(unit, 'toPyObject'):
                unit = unit.toPyObject()
            self._parameter.unit = unit
        return self._parameter

    def set_unit(self):
        """Set up label or combo box for unit."""
        if len(self._parameter.allowed_units) == 1:
            self._unit_widget = QLabel(self._parameter.unit.name)
            self._unit_widget.setToolTip(self._parameter.unit.help_text)
        elif len(self._parameter.allowed_units) > 1:
            self._unit_widget = QComboBox()
            index = -1
            current_index = -1
            for allowed_unit in self._parameter.allowed_units:
                name = allowed_unit.name
                tooltip = allowed_unit.help_text
                index += 1
                if allowed_unit.guid == self._parameter.unit.guid:
                    current_index = index
                self._unit_widget.addItem(name)
                self._unit_widget.setItemData(index, tooltip, Qt.ToolTipRole)
                self._unit_widget.setItemData(index, allowed_unit, Qt.UserRole)
            self._unit_widget.setCurrentIndex(current_index)
            self._unit_widget.setToolTip('Select your preferred unit')
            self._unit_widget.currentIndex()

    def set_value(self, value):
        """Set the value of the input

        :param value: The new value
        :type value: int, float
        """
        self._input.setValue(value)
Exemple #48
0
class EditorAssembly(QWidget):
    """
    Class implementing the editor assembly widget containing the navigation
    combos and the editor widget.
    """
    def __init__(self, dbs, fn=None, vm=None, filetype="", editor=None,
                 tv=None):
        """
        Constructor
        
        @param dbs reference to the debug server object
        @param fn name of the file to be opened (string). If it is None,
            a new (empty) editor is opened
        @param vm reference to the view manager object
            (ViewManager.ViewManager)
        @param filetype type of the source file (string)
        @param editor reference to an Editor object, if this is a cloned view
        @param tv reference to the task viewer object
        """
        super(EditorAssembly, self).__init__()
        
        self.__layout = QGridLayout(self)
        self.__layout.setContentsMargins(0, 0, 0, 0)
        self.__layout.setSpacing(1)
        
        self.__globalsCombo = QComboBox()
        self.__membersCombo = QComboBox()
        from .Editor import Editor
        self.__editor = Editor(dbs, fn, vm, filetype, editor, tv)
        
        self.__layout.addWidget(self.__globalsCombo, 0, 0)
        self.__layout.addWidget(self.__membersCombo, 0, 1)
        self.__layout.addWidget(self.__editor, 1, 0, 1, -1)
        
        self.__module = None
        
        self.__globalsCombo.activated[int].connect(self.__globalsActivated)
        self.__membersCombo.activated[int].connect(self.__membersActivated)
        self.__editor.cursorLineChanged.connect(self.__editorCursorLineChanged)
        
        self.__parseTimer = QTimer(self)
        self.__parseTimer.setSingleShot(True)
        self.__parseTimer.setInterval(5 * 1000)
        self.__parseTimer.timeout.connect(self.__parseEditor)
        self.__editor.textChanged.connect(self.__resetParseTimer)
        self.__editor.refreshed.connect(self.__resetParseTimer)
        
        self.__selectedGlobal = ""
        self.__selectedMember = ""
        self.__globalsBoundaries = {}
        self.__membersBoundaries = {}
        
        QTimer.singleShot(0, self.__parseEditor)
    
    def shutdownTimer(self):
        """
        Public method to stop and disconnect the timer.
        """
        self.__parseTimer.stop()
        self.__parseTimer.timeout.disconnect(self.__parseEditor)
        self.__editor.textChanged.disconnect(self.__resetParseTimer)
        self.__editor.refreshed.disconnect(self.__resetParseTimer)
    
    def getEditor(self):
        """
        Public method to get the reference to the editor widget.
        
        @return reference to the editor widget (Editor)
        """
        return self.__editor
    
    def __globalsActivated(self, index, moveCursor=True):
        """
        Private method to jump to the line of the selected global entry and to
        populate the members combo box.
        
        @param index index of the selected entry (integer)
        @keyparam moveCursor flag indicating to move the editor cursor
            (boolean)
        """
        # step 1: go to the line of the selected entry
        lineno = self.__globalsCombo.itemData(index)
        if lineno is not None:
            if moveCursor:
                txt = self.__editor.text(lineno - 1).rstrip()
                pos = len(txt.replace(txt.strip(), ""))
                self.__editor.gotoLine(
                    lineno, pos if pos == 0 else pos + 1, True)
                self.__editor.setFocus()
            
            # step 2: populate the members combo, if the entry is a class
            self.__membersCombo.clear()
            self.__membersBoundaries = {}
            self.__membersCombo.addItem("")
            memberIndex = 0
            entryName = self.__globalsCombo.itemText(index)
            if self.__module:
                if entryName in self.__module.classes:
                    entry = self.__module.classes[entryName]
                elif entryName in self.__module.modules:
                    entry = self.__module.modules[entryName]
                    # step 2.0: add module classes
                    items = {}
                    for cl in entry.classes.values():
                        if cl.isPrivate():
                            icon = UI.PixmapCache.getIcon("class_private.png")
                        elif cl.isProtected():
                            icon = UI.PixmapCache.getIcon(
                                "class_protected.png")
                        else:
                            icon = UI.PixmapCache.getIcon("class.png")
                        items[cl.name] = (icon, cl.lineno, cl.endlineno)
                    for key in sorted(items.keys()):
                        itm = items[key]
                        self.__membersCombo.addItem(itm[0], key, itm[1])
                        memberIndex += 1
                        self.__membersBoundaries[(itm[1], itm[2])] = \
                            memberIndex
                else:
                    return
                
                # step 2.1: add class methods
                from Utilities.ModuleParser import Function
                items = {}
                for meth in entry.methods.values():
                    if meth.modifier == Function.Static:
                        icon = UI.PixmapCache.getIcon("method_static.png")
                    elif meth.modifier == Function.Class:
                        icon = UI.PixmapCache.getIcon("method_class.png")
                    elif meth.isPrivate():
                        icon = UI.PixmapCache.getIcon("method_private.png")
                    elif meth.isProtected():
                        icon = UI.PixmapCache.getIcon("method_protected.png")
                    else:
                        icon = UI.PixmapCache.getIcon("method.png")
                    items[meth.name] = (icon, meth.lineno, meth.endlineno)
                for key in sorted(items.keys()):
                    itm = items[key]
                    self.__membersCombo.addItem(itm[0], key, itm[1])
                    memberIndex += 1
                    self.__membersBoundaries[(itm[1], itm[2])] = memberIndex
                
                # step 2.2: add class instance attributes
                items = {}
                for attr in entry.attributes.values():
                    if attr.isPrivate():
                        icon = UI.PixmapCache.getIcon("attribute_private.png")
                    elif attr.isProtected():
                        icon = UI.PixmapCache.getIcon(
                            "attribute_protected.png")
                    else:
                        icon = UI.PixmapCache.getIcon("attribute.png")
                    items[attr.name] = (icon, attr.lineno)
                for key in sorted(items.keys()):
                    itm = items[key]
                    self.__membersCombo.addItem(itm[0], key, itm[1])
                
                # step 2.3: add class attributes
                items = {}
                icon = UI.PixmapCache.getIcon("attribute_class.png")
                for glob in entry.globals.values():
                    items[glob.name] = (icon, glob.lineno)
                for key in sorted(items.keys()):
                    itm = items[key]
                    self.__membersCombo.addItem(itm[0], key, itm[1])
    
    def __membersActivated(self, index, moveCursor=True):
        """
        Private method to jump to the line of the selected members entry.
        
        @param index index of the selected entry (integer)
        @keyparam moveCursor flag indicating to move the editor cursor
            (boolean)
        """
        lineno = self.__membersCombo.itemData(index)
        if lineno is not None and moveCursor:
            txt = self.__editor.text(lineno - 1).rstrip()
            pos = len(txt.replace(txt.strip(), ""))
            self.__editor.gotoLine(lineno, pos if pos == 0 else pos + 1, True)
            self.__editor.setFocus()
    
    def __resetParseTimer(self):
        """
        Private slot to reset the parse timer.
        """
        self.__parseTimer.stop()
        self.__parseTimer.start()
    
    def __parseEditor(self):
        """
        Private method to parse the editor source and repopulate the globals
        combo.
        """
        from Utilities.ModuleParser import Module, getTypeFromTypeName
        
        self.__module = None
        sourceType = getTypeFromTypeName(self.__editor.determineFileType())
        if sourceType != -1:
            src = self.__editor.text()
            if src:
                fn = self.__editor.getFileName()
                if fn is None:
                    fn = ""
                self.__module = Module("", fn, sourceType)
                self.__module.scan(src)
                
                # remember the current selections
                self.__selectedGlobal = self.__globalsCombo.currentText()
                self.__selectedMember = self.__membersCombo.currentText()
                
                self.__globalsCombo.clear()
                self.__membersCombo.clear()
                self.__globalsBoundaries = {}
                self.__membersBoundaries = {}
                
                self.__globalsCombo.addItem("")
                index = 0
                
                # step 1: add modules
                items = {}
                for module in self.__module.modules.values():
                    items[module.name] = (UI.PixmapCache.getIcon("module.png"),
                                          module.lineno, module.endlineno)
                for key in sorted(items.keys()):
                    itm = items[key]
                    self.__globalsCombo.addItem(itm[0], key, itm[1])
                    index += 1
                    self.__globalsBoundaries[(itm[1], itm[2])] = index
                
                # step 2: add classes
                items = {}
                for cl in self.__module.classes.values():
                    if cl.isPrivate():
                        icon = UI.PixmapCache.getIcon("class_private.png")
                    elif cl.isProtected():
                        icon = UI.PixmapCache.getIcon("class_protected.png")
                    else:
                        icon = UI.PixmapCache.getIcon("class.png")
                    items[cl.name] = (icon, cl.lineno, cl.endlineno)
                for key in sorted(items.keys()):
                    itm = items[key]
                    self.__globalsCombo.addItem(itm[0], key, itm[1])
                    index += 1
                    self.__globalsBoundaries[(itm[1], itm[2])] = index
                
                # step 3: add functions
                items = {}
                for func in self.__module.functions.values():
                    if func.isPrivate():
                        icon = UI.PixmapCache.getIcon("method_private.png")
                    elif func.isProtected():
                        icon = UI.PixmapCache.getIcon("method_protected.png")
                    else:
                        icon = UI.PixmapCache.getIcon("method.png")
                    items[func.name] = (icon, func.lineno, func.endlineno)
                for key in sorted(items.keys()):
                    itm = items[key]
                    self.__globalsCombo.addItem(itm[0], key, itm[1])
                    index += 1
                    self.__globalsBoundaries[(itm[1], itm[2])] = index
                
                # step 4: add attributes
                items = {}
                for glob in self.__module.globals.values():
                    if glob.isPrivate():
                        icon = UI.PixmapCache.getIcon("attribute_private.png")
                    elif glob.isProtected():
                        icon = UI.PixmapCache.getIcon(
                            "attribute_protected.png")
                    else:
                        icon = UI.PixmapCache.getIcon("attribute.png")
                    items[glob.name] = (icon, glob.lineno)
                for key in sorted(items.keys()):
                    itm = items[key]
                    self.__globalsCombo.addItem(itm[0], key, itm[1])
                
                # reset the currently selected entries without moving the
                # text cursor
                index = self.__globalsCombo.findText(self.__selectedGlobal)
                if index != -1:
                    self.__globalsCombo.setCurrentIndex(index)
                    self.__globalsActivated(index, moveCursor=False)
                index = self.__membersCombo.findText(self.__selectedMember)
                if index != -1:
                    self.__membersCombo.setCurrentIndex(index)
                    self.__membersActivated(index, moveCursor=False)
    
    def __editorCursorLineChanged(self, lineno):
        """
        Private slot handling a line change of the cursor of the editor.
        
        @param lineno line number of the cursor (integer)
        """
        lineno += 1     # cursor position is zero based, code info one based
        
        # step 1: search in the globals
        for (lower, upper), index in self.__globalsBoundaries.items():
            if upper == -1:
                upper = 1000000     # it is the last line
            if lower <= lineno <= upper:
                break
        else:
            index = 0
        self.__globalsCombo.setCurrentIndex(index)
        self.__globalsActivated(index, moveCursor=False)
        
        # step 2: search in members
        for (lower, upper), index in self.__membersBoundaries.items():
            if upper == -1:
                upper = 1000000     # it is the last line
            if lower <= lineno <= upper:
                break
        else:
            index = 0
        self.__membersCombo.setCurrentIndex(index)
        self.__membersActivated(index, moveCursor=False)
Exemple #49
0
class ManageWindow(QWidget):
    __BASE_HEIGHT__ = 400

    signal_user_res = pyqtSignal(bool)
    signal_table_update = pyqtSignal()

    def __init__(self,
                 i18n: dict,
                 icon_cache: MemoryCache,
                 manager: SoftwareManager,
                 disk_cache: bool,
                 download_icons: bool,
                 screen_size,
                 suggestions: bool,
                 display_limit: int,
                 config: Configuration,
                 context: ApplicationContext,
                 notifications: bool,
                 tray_icon=None):
        super(ManageWindow, self).__init__()
        self.i18n = i18n
        self.manager = manager
        self.tray_icon = tray_icon
        self.working = False  # restrict the number of threaded actions
        self.pkgs = []  # packages current loaded in the table
        self.pkgs_available = []  # all packages loaded in memory
        self.pkgs_installed = []  # cached installed packages
        self.display_limit = display_limit
        self.icon_cache = icon_cache
        self.disk_cache = disk_cache
        self.download_icons = download_icons
        self.screen_size = screen_size
        self.config = config
        self.context = context
        self.notifications = notifications

        self.icon_app = QIcon(resource.get_path('img/logo.svg'))
        self.resize(ManageWindow.__BASE_HEIGHT__, ManageWindow.__BASE_HEIGHT__)
        self.setWindowIcon(self.icon_app)

        self.layout = QVBoxLayout()
        self.setLayout(self.layout)

        self.toolbar_top = QToolBar()
        self.toolbar_top.addWidget(new_spacer())

        self.label_status = QLabel()
        self.label_status.setText('')
        self.label_status.setStyleSheet("font-weight: bold")
        self.toolbar_top.addWidget(self.label_status)

        self.toolbar_search = QToolBar()
        self.toolbar_search.setStyleSheet("spacing: 0px;")
        self.toolbar_search.setContentsMargins(0, 0, 0, 0)

        label_pre_search = QLabel()
        label_pre_search.setStyleSheet(
            "background: white; border-top-left-radius: 5px; border-bottom-left-radius: 5px;"
        )
        self.toolbar_search.addWidget(label_pre_search)

        self.input_search = QLineEdit()
        self.input_search.setMaxLength(20)
        self.input_search.setFrame(False)
        self.input_search.setPlaceholderText(
            self.i18n['window_manage.input_search.placeholder'] + "...")
        self.input_search.setToolTip(
            self.i18n['window_manage.input_search.tooltip'])
        self.input_search.setStyleSheet(
            "QLineEdit { background-color: white; color: gray; spacing: 0; height: 30px; font-size: 12px; width: 300px}"
        )
        self.input_search.returnPressed.connect(self.search)
        self.toolbar_search.addWidget(self.input_search)

        label_pos_search = QLabel()
        label_pos_search.setPixmap(QPixmap(
            resource.get_path('img/search.svg')))
        label_pos_search.setStyleSheet(
            "background: white; padding-right: 10px; border-top-right-radius: 5px; border-bottom-right-radius: 5px;"
        )
        self.toolbar_search.addWidget(label_pos_search)

        self.ref_toolbar_search = self.toolbar_top.addWidget(
            self.toolbar_search)
        self.toolbar_top.addWidget(new_spacer())
        self.layout.addWidget(self.toolbar_top)

        self.toolbar = QToolBar()
        self.toolbar.setStyleSheet(
            'QToolBar {spacing: 4px; margin-top: 15px; margin-bottom: 5px}')

        self.checkbox_updates = QCheckBox()
        self.checkbox_updates.setText(self.i18n['updates'].capitalize())
        self.checkbox_updates.stateChanged.connect(self._handle_updates_filter)
        self.ref_checkbox_updates = self.toolbar.addWidget(
            self.checkbox_updates)

        self.checkbox_only_apps = QCheckBox()
        self.checkbox_only_apps.setText(
            self.i18n['manage_window.checkbox.only_apps'])
        self.checkbox_only_apps.setChecked(True)
        self.checkbox_only_apps.stateChanged.connect(
            self._handle_filter_only_apps)
        self.ref_checkbox_only_apps = self.toolbar.addWidget(
            self.checkbox_only_apps)

        self.any_type_filter = 'any'
        self.cache_type_filter_icons = {}
        self.combo_filter_type = QComboBox()
        self.combo_filter_type.setStyleSheet('QLineEdit { height: 2px}')
        self.combo_filter_type.setEditable(True)
        self.combo_filter_type.lineEdit().setReadOnly(True)
        self.combo_filter_type.lineEdit().setAlignment(Qt.AlignCenter)
        self.combo_filter_type.activated.connect(self._handle_type_filter)
        self.combo_filter_type.addItem(
            load_icon(resource.get_path('img/logo.svg'), 14),
            self.i18n[self.any_type_filter].capitalize(), self.any_type_filter)
        self.ref_combo_filter_type = self.toolbar.addWidget(
            self.combo_filter_type)

        self.input_name_filter = InputFilter(self.apply_filters_async)
        self.input_name_filter.setMaxLength(10)
        self.input_name_filter.setPlaceholderText(
            self.i18n['manage_window.name_filter.placeholder'] + '...')
        self.input_name_filter.setToolTip(
            self.i18n['manage_window.name_filter.tooltip'])
        self.input_name_filter.setStyleSheet(
            "QLineEdit { background-color: white; color: gray;}")
        self.input_name_filter.setFixedWidth(130)
        self.ref_input_name_filter = self.toolbar.addWidget(
            self.input_name_filter)

        self.toolbar.addWidget(new_spacer())

        self.bt_installed = QPushButton()
        self.bt_installed.setToolTip(
            self.i18n['manage_window.bt.installed.tooltip'])
        self.bt_installed.setIcon(QIcon(resource.get_path('img/disk.png')))
        self.bt_installed.setText(
            self.i18n['manage_window.bt.installed.text'].capitalize())
        self.bt_installed.clicked.connect(self._show_installed)
        self.bt_installed.setStyleSheet(toolbar_button_style('#A94E0A'))
        self.ref_bt_installed = self.toolbar.addWidget(self.bt_installed)

        self.bt_refresh = QPushButton()
        self.bt_refresh.setToolTip(i18n['manage_window.bt.refresh.tooltip'])
        self.bt_refresh.setIcon(QIcon(resource.get_path('img/refresh.svg')))
        self.bt_refresh.setText(self.i18n['manage_window.bt.refresh.text'])
        self.bt_refresh.setStyleSheet(toolbar_button_style('#2368AD'))
        self.bt_refresh.clicked.connect(
            lambda: self.refresh_apps(keep_console=False))
        self.ref_bt_refresh = self.toolbar.addWidget(self.bt_refresh)

        self.bt_upgrade = QPushButton()
        self.bt_upgrade.setToolTip(i18n['manage_window.bt.upgrade.tooltip'])
        self.bt_upgrade.setIcon(QIcon(resource.get_path('img/app_update.svg')))
        self.bt_upgrade.setText(i18n['manage_window.bt.upgrade.text'])
        self.bt_upgrade.setStyleSheet(toolbar_button_style('#20A435'))
        self.bt_upgrade.clicked.connect(self.update_selected)
        self.ref_bt_upgrade = self.toolbar.addWidget(self.bt_upgrade)

        self.layout.addWidget(self.toolbar)

        self.table_apps = AppsTable(self,
                                    self.icon_cache,
                                    disk_cache=self.disk_cache,
                                    download_icons=self.download_icons)
        self.table_apps.change_headers_policy()

        self.layout.addWidget(self.table_apps)

        toolbar_console = QToolBar()

        self.checkbox_console = QCheckBox()
        self.checkbox_console.setText(
            self.i18n['manage_window.checkbox.show_details'])
        self.checkbox_console.stateChanged.connect(self._handle_console)
        self.checkbox_console.setVisible(False)
        self.ref_checkbox_console = toolbar_console.addWidget(
            self.checkbox_console)

        toolbar_console.addWidget(new_spacer())

        self.label_displayed = QLabel()
        toolbar_console.addWidget(self.label_displayed)

        self.layout.addWidget(toolbar_console)

        self.textarea_output = QPlainTextEdit(self)
        self.textarea_output.resize(self.table_apps.size())
        self.textarea_output.setStyleSheet("background: black; color: white;")
        self.layout.addWidget(self.textarea_output)
        self.textarea_output.setVisible(False)
        self.textarea_output.setReadOnly(True)

        self.toolbar_substatus = QToolBar()
        self.toolbar_substatus.addWidget(new_spacer())
        self.label_substatus = QLabel()
        self.toolbar_substatus.addWidget(self.label_substatus)
        self.toolbar_substatus.addWidget(new_spacer())
        self.layout.addWidget(self.toolbar_substatus)
        self._change_label_substatus('')

        self.thread_update = self._bind_async_action(
            UpdateSelectedApps(self.manager, self.i18n),
            finished_call=self._finish_update_selected)
        self.thread_refresh = self._bind_async_action(
            RefreshApps(self.manager),
            finished_call=self._finish_refresh_apps,
            only_finished=True)
        self.thread_uninstall = self._bind_async_action(
            UninstallApp(self.manager, self.icon_cache),
            finished_call=self._finish_uninstall)
        self.thread_get_info = self._bind_async_action(
            GetAppInfo(self.manager), finished_call=self._finish_get_info)
        self.thread_get_history = self._bind_async_action(
            GetAppHistory(self.manager, self.i18n),
            finished_call=self._finish_get_history)
        self.thread_search = self._bind_async_action(
            SearchPackages(self.manager),
            finished_call=self._finish_search,
            only_finished=True)
        self.thread_downgrade = self._bind_async_action(
            DowngradeApp(self.manager, self.i18n),
            finished_call=self._finish_downgrade)
        self.thread_suggestions = self._bind_async_action(
            FindSuggestions(man=self.manager),
            finished_call=self._finish_search,
            only_finished=True)
        self.thread_run_app = self._bind_async_action(
            LaunchApp(self.manager),
            finished_call=self._finish_run_app,
            only_finished=False)
        self.thread_custom_action = self._bind_async_action(
            CustomAction(manager=self.manager),
            finished_call=self._finish_custom_action)

        self.thread_apply_filters = ApplyFilters()
        self.thread_apply_filters.signal_finished.connect(
            self._finish_apply_filters_async)
        self.thread_apply_filters.signal_table.connect(
            self._update_table_and_upgrades)
        self.signal_table_update.connect(
            self.thread_apply_filters.stop_waiting)

        self.thread_install = InstallPackage(manager=self.manager,
                                             disk_cache=self.disk_cache,
                                             icon_cache=self.icon_cache,
                                             locale_keys=self.i18n)
        self._bind_async_action(self.thread_install,
                                finished_call=self._finish_install)

        self.thread_animate_progress = AnimateProgress()
        self.thread_animate_progress.signal_change.connect(
            self._update_progress)

        self.thread_verify_models = VerifyModels()
        self.thread_verify_models.signal_updates.connect(
            self._notify_model_data_change)

        self.toolbar_bottom = QToolBar()
        self.toolbar_bottom.setIconSize(QSize(16, 16))
        self.toolbar_bottom.setStyleSheet('QToolBar { spacing: 3px }')

        self.toolbar_bottom.addWidget(new_spacer())

        self.progress_bar = QProgressBar()
        self.progress_bar.setMaximumHeight(10 if QApplication.instance().style(
        ).objectName().lower() == 'windows' else 4)

        self.progress_bar.setTextVisible(False)
        self.ref_progress_bar = self.toolbar_bottom.addWidget(
            self.progress_bar)

        self.toolbar_bottom.addWidget(new_spacer())

        self.combo_styles = StylesComboBox(
            parent=self, i18n=i18n, show_panel_after_restart=bool(tray_icon))
        self.combo_styles.setStyleSheet('QComboBox {font-size: 12px;}')
        self.ref_combo_styles = self.toolbar_bottom.addWidget(
            self.combo_styles)

        bt_settings = IconButton(
            icon_path=resource.get_path('img/app_settings.svg'),
            action=self._show_settings_menu,
            background='#12ABAB',
            tooltip=self.i18n['manage_window.bt_settings.tooltip'])
        self.ref_bt_settings = self.toolbar_bottom.addWidget(bt_settings)

        self.layout.addWidget(self.toolbar_bottom)

        qt_utils.centralize(self)

        self.filter_only_apps = True
        self.type_filter = self.any_type_filter
        self.filter_updates = False
        self._maximized = False
        self.progress_controll_enabled = True
        self.recent_installation = False

        self.dialog_about = None
        self.first_refresh = suggestions

        self.thread_warnings = ListWarnings(man=manager, locale_keys=i18n)
        self.thread_warnings.signal_warnings.connect(self._show_warnings)

    def set_tray_icon(self, tray_icon):
        self.tray_icon = tray_icon
        self.combo_styles.show_panel_after_restart = bool(tray_icon)

    def _update_process_progress(self, val: int):
        if self.progress_controll_enabled:
            self.thread_animate_progress.set_progress(val)

    def apply_filters_async(self):
        self.label_status.setText(self.i18n['manage_window.status.filtering'] +
                                  '...')

        self.ref_toolbar_search.setVisible(False)

        if self.ref_input_name_filter.isVisible():
            self.input_name_filter.setReadOnly(True)

        self.thread_apply_filters.filters = self._gen_filters()
        self.thread_apply_filters.pkgs = self.pkgs_available
        self.thread_apply_filters.start()

    def _update_table_and_upgrades(self, pkgs_info: dict):
        self._update_table(pkgs_info=pkgs_info, signal=True)
        self.update_bt_upgrade(pkgs_info)

    def _finish_apply_filters_async(self, success: bool):
        self.label_status.setText('')
        self.ref_toolbar_search.setVisible(True)

        if self.ref_input_name_filter.isVisible():
            self.input_name_filter.setReadOnly(False)

    def _bind_async_action(self,
                           action: AsyncAction,
                           finished_call,
                           only_finished: bool = False) -> AsyncAction:
        action.signal_finished.connect(finished_call)

        if not only_finished:
            action.signal_confirmation.connect(self._ask_confirmation)
            action.signal_output.connect(self._update_action_output)
            action.signal_message.connect(self._show_message)
            action.signal_status.connect(self._change_label_status)
            action.signal_substatus.connect(self._change_label_substatus)
            action.signal_progress.connect(self._update_process_progress)

            self.signal_user_res.connect(action.confirm)

        return action

    def _ask_confirmation(self, msg: dict):
        self.thread_animate_progress.pause()
        diag = ConfirmationDialog(title=msg['title'],
                                  body=msg['body'],
                                  locale_keys=self.i18n,
                                  components=msg['components'],
                                  confirmation_label=msg['confirmation_label'],
                                  deny_label=msg['deny_label'])
        res = diag.is_confirmed()
        self.thread_animate_progress.animate()
        self.signal_user_res.emit(res)

    def _show_message(self, msg: dict):
        self.thread_animate_progress.pause()
        dialog.show_message(title=msg['title'],
                            body=msg['body'],
                            type_=msg['type'])
        self.thread_animate_progress.animate()

    def _show_warnings(self, warnings: List[str]):
        if warnings:
            dialog.show_message(title=self.i18n['warning'].capitalize(),
                                body='<p>{}</p>'.format(
                                    '<br/><br/>'.join(warnings)),
                                type_=MessageType.WARNING)

    def show(self):
        super(ManageWindow, self).show()
        if not self.thread_warnings.isFinished():
            self.thread_warnings.start()

    def verify_warnings(self):
        self.thread_warnings.start()

    def _show_installed(self):
        if self.pkgs_installed:
            self.finish_action()
            self.ref_bt_upgrade.setVisible(True)
            self.ref_checkbox_only_apps.setVisible(True)
            self.input_search.setText('')
            self.input_name_filter.setText('')
            self.update_pkgs(new_pkgs=None, as_installed=True)

    def _show_about(self):
        if self.dialog_about is None:
            self.dialog_about = AboutDialog(self.i18n)

        self.dialog_about.show()

    def _handle_updates_filter(self, status: int):
        self.filter_updates = status == 2
        self.apply_filters_async()

    def _handle_filter_only_apps(self, status: int):
        self.filter_only_apps = status == 2
        self.apply_filters_async()

    def _handle_type_filter(self, idx: int):
        self.type_filter = self.combo_filter_type.itemData(idx)
        self.apply_filters_async()

    def _notify_model_data_change(self):
        self.table_apps.fill_async_data()

    def changeEvent(self, e: QEvent):
        if isinstance(e, QWindowStateChangeEvent):
            self._maximized = self.isMaximized()
            policy = QHeaderView.Stretch if self._maximized else QHeaderView.ResizeToContents
            self.table_apps.change_headers_policy(policy)

    def closeEvent(self, event):

        if self.tray_icon:
            event.ignore()
            self.hide()
            self._handle_console_option(False)

    def _handle_console(self, checked: bool):

        if checked:
            self.textarea_output.show()
        else:
            self.textarea_output.hide()

    def _handle_console_option(self, enable: bool):

        if enable:
            self.textarea_output.clear()

        self.ref_checkbox_console.setVisible(enable)
        self.checkbox_console.setChecked(False)
        self.textarea_output.hide()

    def refresh_apps(self,
                     keep_console: bool = True,
                     top_app: PackageView = None,
                     pkg_types: Set[Type[SoftwarePackage]] = None):
        self.recent_installation = False
        self.type_filter = None
        self.input_search.clear()

        if not keep_console:
            self._handle_console_option(False)

        self.ref_checkbox_updates.setVisible(False)
        self.ref_checkbox_only_apps.setVisible(False)
        self._begin_action(self.i18n['manage_window.status.refreshing'],
                           keep_bt_installed=False,
                           clear_filters=True)

        self.thread_refresh.app = top_app  # the app will be on top when refresh happens
        self.thread_refresh.pkg_types = pkg_types
        self.thread_refresh.start()

    def _finish_refresh_apps(self, res: dict, as_installed: bool = True):
        self.finish_action()
        self.ref_checkbox_only_apps.setVisible(bool(res['installed']))
        self.ref_bt_upgrade.setVisible(True)
        self.update_pkgs(res['installed'],
                         as_installed=as_installed,
                         types=res['types'])
        self.first_refresh = False

    def uninstall_app(self, app: PackageView):
        pwd = None
        requires_root = self.manager.requires_root('uninstall', app.model)

        if not is_root() and requires_root:
            pwd, ok = ask_root_password(self.i18n)

            if not ok:
                return

        self._handle_console_option(True)
        self._begin_action('{} {}'.format(
            self.i18n['manage_window.status.uninstalling'], app.model.name))

        self.thread_uninstall.app = app
        self.thread_uninstall.root_password = pwd
        self.thread_uninstall.start()

    def run_app(self, app: PackageView):
        self._begin_action(
            self.i18n['manage_window.status.running_app'].format(
                app.model.name))
        self.thread_run_app.app = app
        self.thread_run_app.start()

    def _finish_uninstall(self, pkgv: PackageView):
        self.finish_action()

        if pkgv:
            if self._can_notify_user():
                util.notify_user('{} ({}) {}'.format(pkgv.model.name,
                                                     pkgv.model.get_type(),
                                                     self.i18n['uninstalled']))

            self.refresh_apps(pkg_types={pkgv.model.__class__})
        else:
            if self._can_notify_user():
                util.notify_user('{}: {}'.format(
                    pkgv.model.name,
                    self.i18n['notification.uninstall.failed']))

            self.checkbox_console.setChecked(True)

    def _can_notify_user(self):
        return self.notifications and (self.isHidden() or self.isMinimized())

    def _finish_downgrade(self, res: dict):
        self.finish_action()

        if res['success']:
            if self._can_notify_user():
                util.notify_user('{} {}'.format(res['app'],
                                                self.i18n['downgraded']))

            self.refresh_apps(pkg_types={res['app'].model.__class__})

            if self.tray_icon:
                self.tray_icon.verify_updates(notify_user=False)
        else:
            if self._can_notify_user():
                util.notify_user(self.i18n['notification.downgrade.failed'])

            self.checkbox_console.setChecked(True)

    def _change_label_status(self, status: str):
        self.label_status.setText(status)

    def _change_label_substatus(self, substatus: str):
        self.label_substatus.setText('<p>{}</p>'.format(substatus))
        if not substatus:
            self.toolbar_substatus.hide()
        elif not self.toolbar_substatus.isVisible():
            self.toolbar_substatus.show()

    def _update_table(self, pkgs_info: dict, signal: bool = False):
        self.pkgs = pkgs_info['pkgs_displayed']

        self.table_apps.update_pkgs(
            self.pkgs, update_check_enabled=pkgs_info['not_installed'] == 0)

        if not self._maximized:
            self.table_apps.change_headers_policy(QHeaderView.Stretch)
            self.table_apps.change_headers_policy()
            self.resize_and_center(accept_lower_width=len(self.pkgs) > 0)
            self.label_displayed.setText('{} / {}'.format(
                len(self.pkgs), len(self.pkgs_available)))
        else:
            self.label_displayed.setText('')

        if signal:
            self.signal_table_update.emit()

    def update_bt_upgrade(self, pkgs_info: dict = None):
        show_bt_upgrade = False

        if not pkgs_info or pkgs_info['not_installed'] == 0:
            for app_v in (pkgs_info['pkgs_displayed']
                          if pkgs_info else self.pkgs):
                if app_v.update_checked:
                    show_bt_upgrade = True
                    break

        self.ref_bt_upgrade.setVisible(show_bt_upgrade)

    def change_update_state(self,
                            pkgs_info: dict,
                            trigger_filters: bool = True):
        self.update_bt_upgrade(pkgs_info)

        if pkgs_info['updates'] > 0:

            if pkgs_info['not_installed'] == 0:
                if not self.ref_checkbox_updates.isVisible():
                    self.ref_checkbox_updates.setVisible(True)

                if not self.filter_updates:
                    self._change_checkbox(self.checkbox_updates, True,
                                          'filter_updates', trigger_filters)

            if pkgs_info['napp_updates'] > 0 and self.filter_only_apps:
                self._change_checkbox(self.checkbox_only_apps, False,
                                      'filter_only_apps', trigger_filters)
        else:
            self._change_checkbox(self.checkbox_updates, False,
                                  'filter_updates', trigger_filters)

            self.ref_checkbox_updates.setVisible(False)

    def _change_checkbox(self,
                         checkbox: QCheckBox,
                         checked: bool,
                         attr: str = None,
                         trigger: bool = True):
        if not trigger:
            checkbox.blockSignals(True)

        checkbox.setChecked(checked)

        if not trigger:
            setattr(self, attr, checked)
            checkbox.blockSignals(False)

    def _gen_filters(self,
                     updates: int = 0,
                     ignore_updates: bool = False) -> dict:
        return {
            'only_apps':
            self.filter_only_apps,
            'type':
            self.type_filter,
            'updates':
            False if ignore_updates else self.filter_updates,
            'name':
            self.input_name_filter.get_text().lower()
            if self.input_name_filter.get_text() else None,
            'display_limit':
            self.display_limit if updates <= 0 else None
        }

    def update_pkgs(self,
                    new_pkgs: List[SoftwarePackage],
                    as_installed: bool,
                    types: Set[type] = None,
                    ignore_updates: bool = False):
        self.input_name_filter.setText('')
        pkgs_info = commons.new_pkgs_info()
        filters = self._gen_filters(ignore_updates)

        if new_pkgs is not None:
            old_installed = None

            if as_installed:
                old_installed = self.pkgs_installed
                self.pkgs_installed = []

            for pkg in new_pkgs:
                app_model = PackageView(model=pkg)
                commons.update_info(app_model, pkgs_info)
                commons.apply_filters(app_model, filters, pkgs_info)

            if old_installed and types:
                for pkgv in old_installed:
                    if not pkgv.model.__class__ in types:
                        commons.update_info(pkgv, pkgs_info)
                        commons.apply_filters(pkgv, filters, pkgs_info)

        else:  # use installed
            for pkgv in self.pkgs_installed:
                commons.update_info(pkgv, pkgs_info)
                commons.apply_filters(pkgv, filters, pkgs_info)

        if pkgs_info['apps_count'] == 0:
            if self.first_refresh:
                self._begin_search('')
                self.thread_suggestions.start()
                return
            else:
                self._change_checkbox(self.checkbox_only_apps,
                                      False,
                                      'filter_only_apps',
                                      trigger=False)
                self.checkbox_only_apps.setCheckable(False)
        else:
            self.checkbox_only_apps.setCheckable(True)
            self._change_checkbox(self.checkbox_only_apps,
                                  True,
                                  'filter_only_apps',
                                  trigger=False)

        self.change_update_state(pkgs_info=pkgs_info, trigger_filters=False)
        self._apply_filters(pkgs_info, ignore_updates=ignore_updates)
        self.change_update_state(pkgs_info=pkgs_info, trigger_filters=False)

        self.pkgs_available = pkgs_info['pkgs']

        if as_installed:
            self.pkgs_installed = pkgs_info['pkgs']

        self.pkgs = pkgs_info['pkgs_displayed']

        if self.pkgs:
            self.ref_input_name_filter.setVisible(True)

        self._update_type_filters(pkgs_info['available_types'])

        self._update_table(pkgs_info=pkgs_info)

        if new_pkgs:
            self.thread_verify_models.apps = self.pkgs
            self.thread_verify_models.start()

        if self.pkgs_installed:
            self.ref_bt_installed.setVisible(not as_installed
                                             and not self.recent_installation)

        self.resize_and_center(accept_lower_width=self.pkgs_installed)

    def _apply_filters(self, pkgs_info: dict, ignore_updates: bool):
        pkgs_info['pkgs_displayed'] = []
        filters = self._gen_filters(updates=pkgs_info['updates'],
                                    ignore_updates=ignore_updates)
        for pkgv in pkgs_info['pkgs']:
            commons.apply_filters(pkgv, filters, pkgs_info)

    def _update_type_filters(self, available_types: dict = None):

        if available_types is None:
            self.ref_combo_filter_type.setVisible(
                self.combo_filter_type.count() > 1)
        else:
            self.type_filter = self.any_type_filter

            if available_types and len(available_types) > 1:
                if self.combo_filter_type.count() > 1:
                    for _ in range(self.combo_filter_type.count() - 1):
                        self.combo_filter_type.removeItem(1)

                for app_type, icon_path in available_types.items():
                    icon = self.cache_type_filter_icons.get(app_type)

                    if not icon:
                        icon = load_icon(icon_path, 14)
                        self.cache_type_filter_icons[app_type] = icon

                    self.combo_filter_type.addItem(icon, app_type.capitalize(),
                                                   app_type)

                self.ref_combo_filter_type.setVisible(True)
            else:
                self.ref_combo_filter_type.setVisible(False)

    def resize_and_center(self, accept_lower_width: bool = True):
        if self.pkgs:
            new_width = reduce(operator.add, [
                self.table_apps.columnWidth(i)
                for i in range(self.table_apps.columnCount())
            ])

            if self.ref_bt_upgrade.isVisible(
            ) or self.ref_bt_settings.isVisible():
                new_width *= 1.07
        else:
            new_width = self.toolbar_top.width()

        if accept_lower_width or new_width > self.width():
            self.resize(new_width, self.height())

            if self.ref_bt_upgrade.isVisible(
            ) and self.bt_upgrade.visibleRegion().isEmpty():
                self.adjustSize()

        qt_utils.centralize(self)

    def update_selected(self):
        if self.pkgs:
            requires_root = False

            to_update = []

            for app_v in self.pkgs:
                if app_v.update_checked:
                    to_update.append(app_v)

                    if self.manager.requires_root('update', app_v.model):
                        requires_root = True

            if to_update and dialog.ask_confirmation(
                    title=self.i18n['manage_window.upgrade_all.popup.title'],
                    body=self.i18n['manage_window.upgrade_all.popup.body'],
                    locale_keys=self.i18n,
                    widgets=[
                        UpdateToggleButton(
                            None, self, self.i18n, clickable=False)
                    ]):
                pwd = None

                if not is_root() and requires_root:
                    pwd, ok = ask_root_password(self.i18n)

                    if not ok:
                        return

                self._handle_console_option(True)
                self.progress_controll_enabled = len(to_update) == 1
                self._begin_action(self.i18n['manage_window.status.upgrading'])
                self.thread_update.apps_to_update = to_update
                self.thread_update.root_password = pwd
                self.thread_update.start()

    def _finish_update_selected(self, res: dict):
        self.finish_action()

        if res['success']:
            if self._can_notify_user():
                util.notify_user('{} {}'.format(
                    res['updated'],
                    self.i18n['notification.update_selected.success']))

            self.refresh_apps(pkg_types=res['types'])

            if self.tray_icon:
                self.tray_icon.verify_updates()
        else:
            if self._can_notify_user():
                util.notify_user(
                    self.i18n['notification.update_selected.failed'])

            self.ref_bt_upgrade.setVisible(True)
            self.checkbox_console.setChecked(True)

    def _update_action_output(self, output: str):
        self.textarea_output.appendPlainText(output)

    def _begin_action(self,
                      action_label: str,
                      keep_search: bool = False,
                      keep_bt_installed: bool = True,
                      clear_filters: bool = False):
        self.ref_input_name_filter.setVisible(False)
        self.ref_combo_filter_type.setVisible(False)
        self.ref_bt_settings.setVisible(False)
        self.thread_animate_progress.stop = False
        self.thread_animate_progress.start()
        self.ref_progress_bar.setVisible(True)
        self.ref_combo_styles.setVisible(False)

        self.label_status.setText(action_label + "...")
        self.ref_bt_upgrade.setVisible(False)
        self.ref_bt_refresh.setVisible(False)
        self.checkbox_only_apps.setEnabled(False)
        self.table_apps.setEnabled(False)
        self.checkbox_updates.setEnabled(False)

        if not keep_bt_installed:
            self.ref_bt_installed.setVisible(False)
        elif self.ref_bt_installed.isVisible():
            self.ref_bt_installed.setEnabled(False)

        if keep_search:
            self.ref_toolbar_search.setVisible(True)
        else:
            self.ref_toolbar_search.setVisible(False)

        if clear_filters:
            self._update_type_filters({})
        else:
            self.combo_filter_type.setEnabled(False)

    def finish_action(self):
        self.ref_combo_styles.setVisible(True)
        self.thread_animate_progress.stop = True
        self.thread_animate_progress.wait()
        self.ref_progress_bar.setVisible(False)
        self.progress_bar.setValue(0)
        self.progress_bar.setTextVisible(False)

        self._change_label_substatus('')
        self.ref_bt_settings.setVisible(True)

        self.ref_bt_refresh.setVisible(True)
        self.checkbox_only_apps.setEnabled(True)
        self.table_apps.setEnabled(True)
        self.input_search.setEnabled(True)
        self.label_status.setText('')
        self.label_substatus.setText('')
        self.ref_toolbar_search.setVisible(True)
        self.ref_toolbar_search.setEnabled(True)
        self.combo_filter_type.setEnabled(True)
        self.checkbox_updates.setEnabled(True)
        self.progress_controll_enabled = True

        if self.pkgs:
            self.ref_input_name_filter.setVisible(True)
            self.update_bt_upgrade()
            self._update_type_filters()

            if self.ref_bt_installed.isVisible():
                self.ref_bt_installed.setEnabled(True)

    def downgrade(self, pkgv: PackageView):
        pwd = None
        requires_root = self.manager.requires_root('downgrade', pkgv.model)

        if not is_root() and requires_root:
            pwd, ok = ask_root_password(self.i18n)

            if not ok:
                return

        self._handle_console_option(True)
        self._begin_action('{} {}'.format(
            self.i18n['manage_window.status.downgrading'], pkgv.model.name))

        self.thread_downgrade.app = pkgv
        self.thread_downgrade.root_password = pwd
        self.thread_downgrade.start()

    def get_app_info(self, pkg: dict):
        self._handle_console_option(False)
        self._begin_action(self.i18n['manage_window.status.info'])

        self.thread_get_info.app = pkg
        self.thread_get_info.start()

    def get_app_history(self, app: dict):
        self._handle_console_option(False)
        self._begin_action(self.i18n['manage_window.status.history'])

        self.thread_get_history.app = app
        self.thread_get_history.start()

    def _finish_get_info(self, app_info: dict):
        self.finish_action()
        dialog_info = InfoDialog(app=app_info,
                                 icon_cache=self.icon_cache,
                                 locale_keys=self.i18n,
                                 screen_size=self.screen_size)
        dialog_info.exec_()

    def _finish_get_history(self, res: dict):
        self.finish_action()

        if res.get('error'):
            self._handle_console_option(True)
            self.textarea_output.appendPlainText(res['error'])
            self.checkbox_console.setChecked(True)
        else:
            dialog_history = HistoryDialog(res['history'], self.icon_cache,
                                           self.i18n)
            dialog_history.exec_()

    def _begin_search(self, word):
        self._handle_console_option(False)
        self.ref_checkbox_only_apps.setVisible(False)
        self.ref_checkbox_updates.setVisible(False)
        self.filter_updates = False
        self._begin_action('{} {}'.format(
            self.i18n['manage_window.status.searching'], word if word else ''),
                           clear_filters=True)

    def search(self):
        word = self.input_search.text().strip()
        if word:
            self._begin_search(word)
            self.thread_search.word = word
            self.thread_search.start()

    def _finish_search(self, res: dict):
        self.finish_action()

        if not res['error']:
            self.ref_bt_upgrade.setVisible(False)
            self.update_pkgs(res['pkgs_found'],
                             as_installed=False,
                             ignore_updates=True)
        else:
            dialog.show_message(title=self.i18n['warning'].capitalize(),
                                body=self.i18n[res['error']],
                                type_=MessageType.WARNING)

    def install(self, pkg: PackageView):
        pwd = None
        requires_root = self.manager.requires_root('install', pkg.model)

        if not is_root() and requires_root:
            pwd, ok = ask_root_password(self.i18n)

            if not ok:
                return

        self._handle_console_option(True)
        self._begin_action('{} {}'.format(
            self.i18n['manage_window.status.installing'], pkg.model.name))

        self.thread_install.pkg = pkg
        self.thread_install.root_password = pwd
        self.thread_install.start()

    def _finish_install(self, res: dict):
        self.input_search.setText('')
        self.finish_action()

        console_output = self.textarea_output.toPlainText()

        if console_output:
            log_path = '/tmp/bauh/logs/install/{}/{}'.format(
                res['pkg'].model.get_type(), res['pkg'].model.name)
            try:
                Path(log_path).mkdir(parents=True, exist_ok=True)

                log_file = log_path + '/{}.log'.format(int(time.time()))
                with open(log_file, 'w+') as f:
                    f.write(console_output)

                self.textarea_output.appendPlainText(
                    self.i18n['console.install_logs.path'].format(
                        '"{}"'.format(log_file)))
            except:
                self.textarea_output.appendPlainText(
                    "[warning] Could not write install log file to '{}'".
                    format(log_path))

        if res['success']:
            self.recent_installation = True
            if self._can_notify_user():
                util.notify_user(msg='{} ({}) {}'.format(
                    res['pkg'].model.name, res['pkg'].model.get_type(),
                    self.i18n['installed']))

            self._finish_refresh_apps({
                'installed': [res['pkg'].model],
                'total': 1,
                'types': None
            })
            self.ref_bt_installed.setVisible(False)
            self.ref_checkbox_only_apps.setVisible(False)
        else:
            if self._can_notify_user():
                util.notify_user('{}: {}'.format(
                    res['pkg'].model.name,
                    self.i18n['notification.install.failed']))

            self.checkbox_console.setChecked(True)

    def _update_progress(self, value: int):
        self.progress_bar.setValue(value)

    def _finish_run_app(self, success: bool):
        self.finish_action()

    def execute_custom_action(self, pkg: PackageView, action: PackageAction):
        pwd = None

        if not is_root() and action.requires_root:
            pwd, ok = ask_root_password(self.i18n)

            if not ok:
                return

        self._handle_console_option(True)
        self._begin_action('{} {}'.format(self.i18n[action.i18n_status_key],
                                          pkg.model.name))

        self.thread_custom_action.pkg = pkg
        self.thread_custom_action.root_password = pwd
        self.thread_custom_action.custom_action = action
        self.thread_custom_action.start()

    def _finish_custom_action(self, res: dict):
        self.finish_action()
        if res['success']:
            self.refresh_apps(pkg_types={res['pkg'].model.__class__})
        else:
            self.checkbox_console.setChecked(True)

    def show_gems_selector(self):
        gem_panel = GemSelectorPanel(window=self,
                                     manager=self.manager,
                                     i18n=self.i18n,
                                     config=self.config,
                                     show_panel_after_restart=bool(
                                         self.tray_icon))
        gem_panel.show()

    def _show_settings_menu(self):
        menu_row = QMenu()

        if isinstance(self.manager, GenericSoftwareManager):
            action_gems = QAction(self.i18n['manage_window.settings.gems'])
            action_gems.setIcon(self.icon_app)

            action_gems.triggered.connect(self.show_gems_selector)
            menu_row.addAction(action_gems)

        action_about = QAction(self.i18n['manage_window.settings.about'])
        action_about.setIcon(QIcon(resource.get_path('img/about.svg')))
        action_about.triggered.connect(self._show_about)
        menu_row.addAction(action_about)

        menu_row.adjustSize()
        menu_row.popup(QCursor.pos())
        menu_row.exec_()
Exemple #50
0
class GlyphWindow(BaseMainWindow):

    def __init__(self, glyph, parent=None):
        super().__init__(parent)
        self.setAttribute(Qt.WA_DeleteOnClose, False)
        self.setUnifiedTitleAndToolBarOnMac(True)

        self.view = GlyphCanvasView(self)
        # create tools and buttons toolBars
        self._tools = []
        self._toolsToolBar = QToolBar(self.tr("Tools"), self)
        self._toolsToolBar.setMovable(False)
        self._buttons = []
        self._buttonsToolBar = QToolBar(self.tr("Buttons"), self)
        self._buttonsToolBar.setMovable(False)
        self.addToolBar(self._toolsToolBar)
        self.addToolBar(self._buttonsToolBar)

        # http://www.setnode.com/blog/right-aligning-a-button-in-a-qtoolbar/
        self._layersToolBar = QToolBar(self.tr("Layers"), self)
        spacer = QWidget()
        spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self._currentLayerBox = QComboBox(self)
        self._currentLayerBox.currentIndexChanged.connect(
            self._layerChanged)
        self._layersToolBar.addWidget(spacer)
        self._layersToolBar.addWidget(self._currentLayerBox)
        self._layersToolBar.setContentsMargins(0, 0, 2, 0)
        self._layersToolBar.setMovable(False)
        self.addToolBar(self._layersToolBar)

        self.setGlyph(glyph)
        app = QApplication.instance()
        tools = app.drawingTools()
        for index, tool in enumerate(tools):
            action = self.installTool(tool)
            if not index:
                action.trigger()
        app.dispatcher.addObserver(
            self, "_drawingToolRegistered", "drawingToolRegistered")
        # TODO: drawingToolUnregistered
        self.installButton(RemoveOverlapButton)

        self.setCentralWidget(self.view)
        self.view.setFocus(True)

        self.readSettings()

    def readSettings(self):
        geometry = settings.glyphWindowGeometry()
        if geometry:
            self.restoreGeometry(geometry)

    def writeSettings(self):
        # TODO: save current tool?
        settings.setGlyphWindowGeometry(self.saveGeometry())

    def setupMenu(self, menuBar):
        fileMenu = menuBar.fetchMenu(Entries.File)
        fontWindow = self.parent()
        if fontWindow is not None:
            fileMenu.fetchAction(Entries.File_Save, fontWindow.saveFile)
            fileMenu.fetchAction(Entries.File_Save_As, fontWindow.saveFileAs)
        fileMenu.fetchAction(Entries.File_Close, self.close)
        if fontWindow is not None:
            fileMenu.fetchAction(Entries.File_Reload, fontWindow.reloadFile)

        editMenu = menuBar.fetchMenu(Entries.Edit)
        self._undoAction = editMenu.fetchAction(Entries.Edit_Undo, self.undo)
        self._redoAction = editMenu.fetchAction(Entries.Edit_Redo, self.redo)
        editMenu.addSeparator()
        cutAction = editMenu.fetchAction(Entries.Edit_Cut, self.cutOutlines)
        copyAction = editMenu.fetchAction(Entries.Edit_Copy, self.copyOutlines)
        self._selectActions = (cutAction, copyAction)
        editMenu.fetchAction(Entries.Edit_Paste, self.pasteOutlines)
        editMenu.fetchAction(Entries.Edit_Select_All, self.selectAll)
        editMenu.fetchAction(Entries.Edit_Find, self.changeGlyph)
        # TODO: sort this out
        # editMenu.fetchAction(self.tr("&Deselect"), self.deselect, "Ctrl+D")

        # glyphMenu = menuBar.fetchMenu(self.tr("&Glyph"))
        # self._layerAction = glyphMenu.fetchAction(
        #     self.tr("&Layer Actions…"), self.layerActions, "L")

        viewMenu = menuBar.fetchMenu(Entries.View)
        viewMenu.fetchAction(Entries.View_Zoom_In, lambda: self.view.zoom(1))
        viewMenu.fetchAction(Entries.View_Zoom_Out, lambda: self.view.zoom(-1))
        viewMenu.fetchAction(Entries.View_Reset_Zoom, self.view.fitScaleBBox)
        viewMenu.addSeparator()
        viewMenu.fetchAction(
            Entries.View_Next_Glyph, lambda: self.glyphOffset(1))
        viewMenu.fetchAction(
            Entries.View_Previous_Glyph, lambda: self.glyphOffset(-1))

        self._updateUndoRedo()

    # ----------
    # Menu items
    # ----------

    def saveFile(self):
        glyph = self.view.glyph()
        font = glyph.font
        if None not in (font, font.path):
            font.save()

    def glyphOffset(self, offset):
        currentGlyph = self.view.glyph()
        font = currentGlyph.font
        glyphOrder = font.glyphOrder
        # should be enforced in fontView already
        if not (glyphOrder and len(glyphOrder)):
            return
        index = glyphOrder.index(currentGlyph.name)
        newIndex = (index + offset) % len(glyphOrder)
        glyph = font[glyphOrder[newIndex]]
        self.setGlyph(glyph)

    def changeGlyph(self):
        glyph = self.view.glyph()
        newGlyph, ok = FindDialog.getNewGlyph(self, glyph)
        if ok and newGlyph is not None:
            self.setGlyph(newGlyph)

    def layerActions(self):
        glyph = self.view.glyph()
        newLayer, action, ok = LayerActionsDialog.getLayerAndAction(
            self, glyph)
        if ok and newLayer is not None:
            # TODO: whole glyph for now, but consider selection too
            if glyph.name not in newLayer:
                newLayer.newGlyph(glyph.name)
            otherGlyph = newLayer[glyph.name]
            otherGlyph.holdNotifications()
            if action == "Swap":
                tempGlyph = glyph.__class__()
                otherGlyph.drawPoints(tempGlyph.getPointPen())
                tempGlyph.width = otherGlyph.width
                otherGlyph.clearContours()
            glyph.drawPoints(otherGlyph.getPointPen())
            otherGlyph.width = glyph.width
            if action != "Copy":
                glyph.holdNotifications()
                glyph.clearContours()
                if action == "Swap":
                    tempGlyph.drawPoints(glyph.getPointPen())
                    glyph.width = tempGlyph.width
                glyph.releaseHeldNotifications()
            otherGlyph.releaseHeldNotifications()

    def undo(self):
        glyph = self.view.glyph()
        glyph.undo()

    def redo(self):
        glyph = self.view.glyph()
        glyph.redo()

    def cutOutlines(self):
        glyph = self.view.glyph()
        self.copyOutlines()
        deleteUISelection(glyph)

    def copyOutlines(self):
        glyph = self.view.glyph()
        clipboard = QApplication.clipboard()
        mimeData = QMimeData()
        copyGlyph = glyph.getRepresentation("TruFont.FilterSelection")
        mimeData.setData("application/x-trufont-glyph-data",
                         pickle.dumps([copyGlyph.serialize(
                             blacklist=("name", "unicode")
                         )]))
        clipboard.setMimeData(mimeData)

    def pasteOutlines(self):
        glyph = self.view.glyph()
        clipboard = QApplication.clipboard()
        mimeData = clipboard.mimeData()
        if mimeData.hasFormat("application/x-trufont-glyph-data"):
            data = pickle.loads(mimeData.data(
                "application/x-trufont-glyph-data"))
            if len(data) == 1:
                pen = glyph.getPointPen()
                pasteGlyph = glyph.__class__()
                pasteGlyph.deserialize(data[0])
                # TODO: if we serialize selected state, we don't need to do
                # this
                pasteGlyph.selected = True
                if len(pasteGlyph) or len(pasteGlyph.components) or \
                        len(pasteGlyph.anchors):
                    glyph.prepareUndo()
                    pasteGlyph.drawPoints(pen)

    def selectAll(self):
        glyph = self.view.glyph()
        if glyph.selected:
            for anchor in glyph.anchors:
                anchor.selected = True
            for component in glyph.components:
                component.selected = True
        else:
            glyph.selected = True

    def deselect(self):
        glyph = self.view.glyph()
        for anchor in glyph.anchors:
            anchor.selected = False
        for component in glyph.components:
            component.selected = False
        glyph.selected = False

    def lockToolBars(self):
        action = self.sender()
        movable = not action.isChecked()
        for toolBar in (
                self._toolsToolBar, self._buttonsToolBar, self._layersToolBar):
            toolBar.setMovable(movable)

    # --------------------------
    # Tools & buttons management
    # --------------------------

    def installTool(self, tool):
        action = self._toolsToolBar.addAction(
            QIcon(tool.iconPath), tool.name, self._setViewTool)
        action.setCheckable(True)
        num = len(self._tools)
        action.setData(num)
        action.setShortcut(QKeySequence(str(num + 1)))
        self._tools.append(tool(parent=self.view.widget()))
        return action

    def uninstallTool(self, tool):
        pass  # XXX

    def _setViewTool(self):
        action = self.sender()
        index = action.data()
        newTool = self._tools[index]
        if newTool == self.view.currentTool():
            action.setChecked(True)
            return
        ok = self.view.setCurrentTool(newTool)
        # if view did change tool, disable them all and enable the one we want
        # otherwise, just disable the tool that was clicked.
        # previously we used QActionGroup to have exclusive buttons, but doing
        # it manually allows us to NAK a button change.
        if ok:
            for act in self._toolsToolBar.actions():
                act.setChecked(False)
        action.setChecked(ok)

    def installButton(self, button):
        action = self._buttonsToolBar.addAction(
            QIcon(button.iconPath), button.name, self._buttonAction)
        action.setData(len(self._buttons))
        self._buttons.append(button(parent=self.view))
        return action

    def uninstallButton(self, button):
        pass  # XXX

    def _buttonAction(self):
        action = self.sender()
        index = action.data()
        button = self._buttons[index]
        button.clicked()

    # -------------
    # Notifications
    # -------------

    # app

    def _drawingToolRegistered(self, notification):
        tool = notification.data["tool"]
        self.installTool(tool)

    # glyph

    def _subscribeToGlyph(self, glyph):
        if glyph is not None:
            glyph.addObserver(self, "_glyphNameChanged", "Glyph.NameChanged")
            glyph.addObserver(
                self, "_glyphSelectionChanged", "Glyph.SelectionChanged")
            undoManager = glyph.undoManager
            undoManager.canUndoChanged.connect(self._setUndoEnabled)
            undoManager.canRedoChanged.connect(self._setRedoEnabled)
            self._subscribeToFontAndLayerSet(glyph.font)

    def _unsubscribeFromGlyph(self, glyph):
        if glyph is not None:
            glyph.removeObserver(self, "Glyph.NameChanged")
            glyph.removeObserver(self, "Glyph.SelectionChanged")
            undoManager = glyph.undoManager
            undoManager.canUndoChanged.disconnect(self._setUndoEnabled)
            undoManager.canRedoChanged.disconnect(self._setRedoEnabled)
            self._unsubscribeFromFontAndLayerSet(glyph.font)

    def _glyphNameChanged(self, notification):
        glyph = self.view.glyph()
        self.setWindowTitle(glyph.name, glyph.font)

    def _glyphSelectionChanged(self, notification):
        self._updateSelection()

    # layers & font

    def _subscribeToFontAndLayerSet(self, font):
        """Note: called by _subscribeToGlyph."""
        if font is None:
            return
        font.info.addObserver(self, "_fontInfoChanged", "Info.Changed")
        layerSet = font.layers
        if layerSet is None:
            return
        layerSet.addObserver(
            self, '_layerSetLayerDeleted', 'LayerSet.LayerDeleted')
        for event in ('LayerSet.LayerAdded', 'LayerSet.LayerChanged',
                      'LayerSet.LayerOrderChanged'):
            layerSet.addObserver(self, '_layerSetEvents', event)

    def _unsubscribeFromFontAndLayerSet(self, font):
        """Note: called by _unsubscribeFromGlyph."""
        if font is None:
            return
        font.info.removeObserver(self, "Info.Changed")
        layerSet = font.layers
        if layerSet is None:
            return
        for event in ('LayerSet.LayerAdded', 'LayerSet.LayerChanged',
                      'LayerSet.LayerOrderChanged', 'LayerSet.LayerDeleted'):
            layerSet.removeObserver(self, event)

    def _fontInfoChanged(self, notification):
        glyph = self.view.glyph()
        self.setWindowTitle(glyph.name, glyph.font)

    def _layerSetEvents(self, notification):
        self._updateLayerControls()

    def _layerSetLayerDeleted(self, notification):
        self._layerSetEvents(notification)
        self._currentLayerBox.setCurrentIndex(0)

    # other updaters

    def _updateSelection(self):
        def hasSelection():
            glyph = self.view.glyph()
            for contour in glyph:
                if len(contour.selection):
                    return True
            for anchor in glyph.anchors:
                if anchor.selected:
                    return True
            for component in glyph.components:
                if component.selected:
                    return True
            return False

        if not hasattr(self, "_selectActions"):
            return
        hasSelection = hasSelection()
        for action in self._selectActions:
            action.setEnabled(hasSelection)

    def _updateUndoRedo(self):
        glyph = self.view.glyph()
        self._setUndoEnabled(glyph.canUndo())
        self._setRedoEnabled(glyph.canRedo())

    def _setUndoEnabled(self, value):
        if not hasattr(self, "_undoAction"):
            return
        self._undoAction.setEnabled(value)

    def _setRedoEnabled(self, value):
        if not hasattr(self, "_redoAction"):
            return
        self._redoAction.setEnabled(value)

    # --------------
    # Public Methods
    # --------------

    def setGlyph(self, glyph):
        currentGlyph = self.view.glyph()
        self._unsubscribeFromGlyph(currentGlyph)
        self.view.setGlyph(glyph)
        self._subscribeToGlyph(glyph)
        self._updateLayerControls()
        self._updateUndoRedo()
        self._updateSelection()
        self.setWindowTitle(glyph.name, glyph.font)
        # setting the layer-glyph here
        app = QApplication.instance()
        app.setCurrentGlyph(glyph)

    # -----------------
    # Layers management
    # -----------------

    def _layerChanged(self, newLayerIndex):
        glyph = self.view.glyph()
        layer = self._currentLayerBox.itemData(newLayerIndex)
        if layer is None:
            layer = self._makeLayer()
            if layer is None:
                # restore comboBox to active index
                layerSet = glyph.layerSet
                index = layerSet.layerOrder.index(glyph.layer.name)
                self._setLayerBoxIndex(index)
                return

        if glyph.name in layer:
            newGlyph = layer[glyph.name]
        else:
            # TODO: make sure we mimic defcon ufo3 APIs for that
            newGlyph = self._makeLayerGlyph(layer, glyph)
        self.setGlyph(newGlyph)

    def _makeLayer(self):
        # TODO: what with duplicate names?
        glyph = self.view.glyph()
        newLayerName, color, ok = AddLayerDialog.getNewLayerNameAndColor(self)
        if ok:
            layerSet = glyph.layerSet
            layer = layerSet.newLayer(newLayerName)
            layer.color = color
            return layer
        return None

    def _makeLayerGlyph(self, layer, currentGlyph):
        glyph = layer.newGlyph(currentGlyph.name)
        glyph.width = currentGlyph.width
        glyph.template = True
        return glyph

    def _updateLayerControls(self):
        comboBox = self._currentLayerBox
        glyph = self.view.glyph()
        comboBox.blockSignals(True)
        comboBox.clear()
        for layer in glyph.layerSet:
            comboBox.addItem(layer.name, layer)
        comboBox.setCurrentText(glyph.layer.name)
        comboBox.addItem(self.tr("New layer…"), None)
        comboBox.blockSignals(False)
        if not hasattr(self, "_layerAction"):
            return
        self._layerAction.setEnabled(len(glyph.layerSet) > 1)

    def _setLayerBoxIndex(self, index):
        comboBox = self._currentLayerBox
        comboBox.blockSignals(True)
        comboBox.setCurrentIndex(index)
        comboBox.blockSignals(False)

    # ---------------------
    # QMainWindow functions
    # ---------------------

    def sizeHint(self):
        return QSize(1100, 750)

    def moveEvent(self, event):
        self.writeSettings()

    resizeEvent = moveEvent

    def event(self, event):
        if event.type() == QEvent.WindowActivate:
            app = QApplication.instance()
            app.setCurrentGlyph(self.view.glyph())
        return super().event(event)

    def showEvent(self, event):
        app = QApplication.instance()
        data = dict(window=self)
        app.postNotification("glyphWindowWillOpen", data)
        super().showEvent(event)
        app.postNotification("glyphWindowOpened", data)

    def closeEvent(self, event):
        super().closeEvent(event)
        if event.isAccepted():
            app = QApplication.instance()
            app.dispatcher.removeObserver(self, "drawingToolRegistered")
            data = dict(window=self)
            app.postNotification("glyphWindowWillClose", data)
            glyph = self.view.glyph()
            self._unsubscribeFromGlyph(glyph)
            self.view.closeEvent(event)

    def setWindowTitle(self, title, font=None):
        if font is not None:
            title = "%s – %s %s" % (
                title, font.info.familyName, font.info.styleName)
        super().setWindowTitle(title)
Exemple #51
0
class HeapPluginForm(PluginForm):
    def __init__(self):
        super(HeapPluginForm, self).__init__()
        self.parent = None
        self.config_path = None
        self.config = None
        self.tracer = None
        self.heap = None
        self.cur_arena = None  # default: main_arena
        self.ptr_size = get_arch_ptrsize()

    def OnCreate(self, form):
        self.parent = self.FormToPyQtWidget(form)
        self.setup_gui()
        self.init_heap()
        self.populate_gui()

    def setup_gui(self):
        self.chunk_widget = ChunkWidget(self)
        self.tracer_tab = TracerWidget(self)
        self.arena_widget = ArenaWidget(self)
        self.bins_widget = BinsWidget(self)
        self.tcache_widget = TcacheWidget(self)
        self.magic_widget = MagicWidget(self)
        self.config_widget = ConfigWidget(self)

        self.tabs = QTabWidget()
        self.tabs.addTab(self.tracer_tab, "Tracer")
        self.tabs.addTab(self.arena_widget, "Arena")
        self.tabs.addTab(self.bins_widget, "Bins")
        self.tabs.addTab(self.tcache_widget, "Tcache")
        self.tabs.addTab(self.magic_widget, "Magic")
        self.tabs.addTab(self.config_widget, "Config")

        self.btn_reload = QPushButton("Reload info")
        icon = QtGui.QIcon(os.path.normpath(ICONS_DIR + '/refresh.png'))
        self.btn_reload.setIcon(icon)
        self.btn_reload.setFixedWidth(120)
        self.btn_reload.setEnabled(False)
        self.btn_reload.clicked.connect(self.reload_gui_info)

        self.cb_arenas = QComboBox()
        self.cb_arenas.setFixedWidth(150)
        self.cb_arenas.currentIndexChanged[int].connect(self.cb_arenas_changed)

        hbox_arenas = QHBoxLayout()
        hbox_arenas.addWidget(QLabel('Switch arena: '))
        hbox_arenas.addWidget(self.cb_arenas)
        hbox_arenas.setContentsMargins(0, 0, 0, 0)

        self.arenas_widget = QWidget()
        self.arenas_widget.setLayout(hbox_arenas)
        self.arenas_widget.setVisible(False)

        self.txt_warning = QLabel()
        self.txt_warning.setStyleSheet("font-weight: bold; color: red")
        self.txt_warning.setVisible(False)

        hbox_top = QHBoxLayout()
        hbox_top.addWidget(self.btn_reload)
        hbox_top.addWidget(self.arenas_widget)
        hbox_top.addWidget(self.txt_warning)
        hbox_top.setContentsMargins(0, 0, 0, 0)
        hbox_top.addStretch(1)

        vbox_left_panel = QVBoxLayout()
        vbox_left_panel.addLayout(hbox_top)
        vbox_left_panel.addWidget(self.tabs)
        vbox_left_panel.setContentsMargins(0, 0, 0, 0)

        left_panel = QWidget()
        left_panel.setLayout(vbox_left_panel)

        self.splitter = QSplitter(QtCore.Qt.Horizontal)
        self.splitter.addWidget(left_panel)
        self.splitter.addWidget(self.chunk_widget)
        self.splitter.setStretchFactor(0, 1)

        main_layout = QVBoxLayout()
        main_layout.addWidget(self.splitter)
        self.parent.setLayout(main_layout)

    def populate_gui(self):
        self.magic_widget.populate_libc_offsets()
        self.reload_gui_info()

    def reload_gui_info(self, from_arena_cb=False):
        if self.heap is None:
            return

        if not is_process_suspended():
            answer = askyn_c(
                ASKBTN_YES,
                "HIDECANCEL\nThe process must be suspended to reload the info.\n\
                Do you want to suspend it?")

            if answer == ASKBTN_NO:
                return

            if not suspend_process():
                warning("Unable to suspend the process")
                return
        try:
            RefreshDebuggerMemory()
            if not self.heap.get_heap_base():
                self.show_warning("Heap not initialized")
                return

            if not get_libc_base():
                self.show_warning("Unable to resolve glibc base address.")
                return

            self.hide_warning()
            self.arenas_widget.setVisible(True)

            if not from_arena_cb:
                self.populate_arenas()

            self.arena_widget.populate_table()
            self.tcache_widget.populate_table()
            self.bins_widget.populate_tables()

        except Exception as e:
            self.show_warning(e.message)
            warning(traceback.format_exc())

    def init_heap(self):
        try:
            self.config_path = CONFIG_PATH
            self.config = HeapConfig(self.config_path)
            self.config_widget.load_config()

        except Exception as e:
            self.config = None
            self.show_warning('Please, update the config file')
            warning(str(e))
            return

        if not self.config.offsets:
            arch_bits = self.ptr_size * 8
            self.show_warning('Config: Libc offsets for %d bits not found.' %
                              arch_bits)
            return

        try:
            current_libc_version = get_libc_version()
            if self.config.libc_version == current_libc_version or current_libc_version == None:
                self.heap = Heap(self.config)
                self.btn_reload.setEnabled(True)
                self.tabs.setTabEnabled(3, self.heap.tcache_enabled)
            else:
                self.show_warning('Config: glibc version mismatched: %s != %s' % \
                    (str(self.config.libc_version), str(current_libc_version)))

        except AttributeError as e:
            warning(str(e))
            self.show_warning('Invalid config file content')

    def populate_arenas(self):
        old_arena = self.cur_arena
        self.cb_arenas.blockSignals(True)
        self.cb_arenas.clear()

        for addr, arena in self.heap.arenas():
            if addr == self.heap.main_arena_addr:
                self.cb_arenas.addItem("main_arena", None)
                self.cb_arenas.addItem("main_arena2", 1)
            else:
                self.cb_arenas.addItem("0x%x" % addr, addr)

        idx = self.cb_arenas.findData(old_arena)
        if idx != -1:
            self.cb_arenas.setCurrentIndex(idx)
        self.cb_arenas.blockSignals(False)

    def show_warning(self, txt):
        self.txt_warning.setText(txt)
        self.txt_warning.setVisible(True)

    def hide_warning(self):
        self.txt_warning.setVisible(False)

    def cb_arenas_changed(self, idx):
        self.cur_arena = self.cb_arenas.itemData(idx)
        self.reload_gui_info(True)

    def show_chunk_info(self, address):
        self.chunk_widget.show_chunk(address)

    def Show(self):
        return PluginForm.Show(self,
                               PLUGNAME,
                               options=(PluginForm.FORM_TAB
                                        | PluginForm.FORM_CLOSE_LATER))

    def OnClose(self, form):
        if self.tracer:
            self.tracer.unhook()
            log("Tracer disabled")
        log("Form closed")
class EditorGeneral(QWidget):

    def __init__(self, parent):
        super().__init__(parent)
        self._preferences = parent
        vbox = QVBoxLayout(self)
        self._font = settings.FONT

        # Group widgets
        group_typo = QGroupBox(
            translations.TR_PREFERENCES_EDITOR_GENERAL_TYPOGRAPHY)
        group_scheme = QGroupBox("Editor Color Scheme")

        # Font settings
        grid_typo = QGridLayout(group_typo)
        self._btn_editor_font = QPushButton('')
        grid_typo.addWidget(QLabel(
            translations.TR_PREFERENCES_EDITOR_GENERAL_EDITOR_FONT), 0, 0)
        grid_typo.addWidget(self._btn_editor_font, 0, 1)
        self._check_font_antialiasing = QCheckBox("Antialiasing")
        grid_typo.addWidget(self._check_font_antialiasing, 1, 0)

        # Scheme settings
        box = QVBoxLayout(group_scheme)
        self._combo_themes = QComboBox()
        box.addWidget(self._combo_themes)
        schemes = json_manager.load_editor_schemes()
        for scheme_name, colors in schemes.items():
            self._combo_themes.addItem(scheme_name, colors)
        self.__current_scheme = settings.EDITOR_SCHEME

        # self._list_view_scheme = QListView()
        # schemes = json_manager.load_editor_schemes()
        # from collections import namedtuple
        # CategoryEntry = namedtuple('CategoryEntry', 'name color')
        # list_of_categories = []
        # for scheme_name, categories in schemes.items():
        #     for category_name in categories.keys():
        #         category = CategoryEntry(
        #             category_name,
        #             categories[category_name]['color']
        #         )
        #         list_of_categories.append(category)

        # model = ListModelScheme(list_of_categories)
        # model.set_font(self._font)
        # self._list_view_scheme.setModel(model)
        # box.addWidget(self._list_view_scheme)
        # Add group widgets
        vbox.addWidget(group_typo)
        vbox.addWidget(group_scheme)
        vbox.addItem(
            QSpacerItem(0, 0, QSizePolicy.Expanding, QSizePolicy.Expanding))
        # Initial Settings
        btn_text = ', '.join(self._font.toString().split(',')[0:2])
        self._btn_editor_font.setText(btn_text)
        self._check_font_antialiasing.setChecked(settings.FONT_ANTIALIASING)
        self._combo_themes.setCurrentText(settings.EDITOR_SCHEME)
        # Connections
        self._btn_editor_font.clicked.connect(self._load_editor_font)
        self._preferences.savePreferences.connect(self._save)

    def _load_editor_font(self):
        font, ok = QFontDialog.getFont(self._font, self)
        if ok:
            self._font = font
            btn_text = ', '.join(self._font.toString().split(',')[0:2])
            self._btn_editor_font.setText(btn_text)

    def _save(self):
        qsettings = IDE.editor_settings()

        settings.FONT = self._font
        qsettings.setValue("editor/general/default_font", settings.FONT)
        settings.FONT_ANTIALIASING = self._check_font_antialiasing.isChecked()
        qsettings.setValue("editor/general/font_antialiasing",
                           settings.FONT_ANTIALIASING)
        settings.EDITOR_SCHEME = self._combo_themes.currentText()
        qsettings.setValue("editor/general/scheme",
                           settings.EDITOR_SCHEME)

        scheme = self._combo_themes.currentText()
        if scheme != self.__current_scheme:
            index = self._combo_themes.currentIndex()
            colors = self._combo_themes.itemData(index)
            resources.COLOR_SCHEME = colors
            main = IDE.get_service("main_container")
            main.restyle_editor()
Exemple #53
0
class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()

        self.renderArea = RenderArea()

        self.shapeComboBox = QComboBox()
        self.shapeComboBox.addItem("Polygon", RenderArea.Polygon)
        self.shapeComboBox.addItem("Rectangle", RenderArea.Rect)
        self.shapeComboBox.addItem("Rounded Rectangle", RenderArea.RoundedRect)
        self.shapeComboBox.addItem("Ellipse", RenderArea.Ellipse)
        self.shapeComboBox.addItem("Pie", RenderArea.Pie)
        self.shapeComboBox.addItem("Chord", RenderArea.Chord)
        self.shapeComboBox.addItem("Path", RenderArea.Path)
        self.shapeComboBox.addItem("Line", RenderArea.Line)
        self.shapeComboBox.addItem("Polyline", RenderArea.Polyline)
        self.shapeComboBox.addItem("Arc", RenderArea.Arc)
        self.shapeComboBox.addItem("Points", RenderArea.Points)
        self.shapeComboBox.addItem("Text", RenderArea.Text)
        self.shapeComboBox.addItem("Pixmap", RenderArea.Pixmap)

        shapeLabel = QLabel("&Shape:")
        shapeLabel.setBuddy(self.shapeComboBox)

        self.penWidthSpinBox = QSpinBox()
        self.penWidthSpinBox.setRange(0, 20)
        self.penWidthSpinBox.setSpecialValueText("0 (cosmetic pen)")

        penWidthLabel = QLabel("Pen &Width:")
        penWidthLabel.setBuddy(self.penWidthSpinBox)

        self.penStyleComboBox = QComboBox()
        self.penStyleComboBox.addItem("Solid", Qt.SolidLine)
        self.penStyleComboBox.addItem("Dash", Qt.DashLine)
        self.penStyleComboBox.addItem("Dot", Qt.DotLine)
        self.penStyleComboBox.addItem("Dash Dot", Qt.DashDotLine)
        self.penStyleComboBox.addItem("Dash Dot Dot", Qt.DashDotDotLine)
        self.penStyleComboBox.addItem("None", Qt.NoPen)

        penStyleLabel = QLabel("&Pen Style:")
        penStyleLabel.setBuddy(self.penStyleComboBox)

        self.penCapComboBox = QComboBox()
        self.penCapComboBox.addItem("Flat", Qt.FlatCap)
        self.penCapComboBox.addItem("Square", Qt.SquareCap)
        self.penCapComboBox.addItem("Round", Qt.RoundCap)

        penCapLabel = QLabel("Pen &Cap:")
        penCapLabel.setBuddy(self.penCapComboBox)

        self.penJoinComboBox = QComboBox()
        self.penJoinComboBox.addItem("Miter", Qt.MiterJoin)
        self.penJoinComboBox.addItem("Bevel", Qt.BevelJoin)
        self.penJoinComboBox.addItem("Round", Qt.RoundJoin)

        penJoinLabel = QLabel("Pen &Join:")
        penJoinLabel.setBuddy(self.penJoinComboBox)

        self.brushStyleComboBox = QComboBox()
        self.brushStyleComboBox.addItem("Linear Gradient",
                                        Qt.LinearGradientPattern)
        self.brushStyleComboBox.addItem("Radial Gradient",
                                        Qt.RadialGradientPattern)
        self.brushStyleComboBox.addItem("Conical Gradient",
                                        Qt.ConicalGradientPattern)
        self.brushStyleComboBox.addItem("Texture", Qt.TexturePattern)
        self.brushStyleComboBox.addItem("Solid", Qt.SolidPattern)
        self.brushStyleComboBox.addItem("Horizontal", Qt.HorPattern)
        self.brushStyleComboBox.addItem("Vertical", Qt.VerPattern)
        self.brushStyleComboBox.addItem("Cross", Qt.CrossPattern)
        self.brushStyleComboBox.addItem("Backward Diagonal", Qt.BDiagPattern)
        self.brushStyleComboBox.addItem("Forward Diagonal", Qt.FDiagPattern)
        self.brushStyleComboBox.addItem("Diagonal Cross", Qt.DiagCrossPattern)
        self.brushStyleComboBox.addItem("Dense 1", Qt.Dense1Pattern)
        self.brushStyleComboBox.addItem("Dense 2", Qt.Dense2Pattern)
        self.brushStyleComboBox.addItem("Dense 3", Qt.Dense3Pattern)
        self.brushStyleComboBox.addItem("Dense 4", Qt.Dense4Pattern)
        self.brushStyleComboBox.addItem("Dense 5", Qt.Dense5Pattern)
        self.brushStyleComboBox.addItem("Dense 6", Qt.Dense6Pattern)
        self.brushStyleComboBox.addItem("Dense 7", Qt.Dense7Pattern)
        self.brushStyleComboBox.addItem("None", Qt.NoBrush)

        brushStyleLabel = QLabel("&Brush Style:")
        brushStyleLabel.setBuddy(self.brushStyleComboBox)

        otherOptionsLabel = QLabel("Other Options:")
        self.antialiasingCheckBox = QCheckBox("&Antialiasing")
        self.transformationsCheckBox = QCheckBox("&Transformations")

        self.shapeComboBox.activated.connect(self.shapeChanged)
        self.penWidthSpinBox.valueChanged.connect(self.penChanged)
        self.penStyleComboBox.activated.connect(self.penChanged)
        self.penCapComboBox.activated.connect(self.penChanged)
        self.penJoinComboBox.activated.connect(self.penChanged)
        self.brushStyleComboBox.activated.connect(self.brushChanged)
        self.antialiasingCheckBox.toggled.connect(
            self.renderArea.setAntialiased)
        self.transformationsCheckBox.toggled.connect(
            self.renderArea.setTransformed)

        mainLayout = QGridLayout()
        mainLayout.setColumnStretch(0, 1)
        mainLayout.setColumnStretch(3, 1)
        mainLayout.addWidget(self.renderArea, 0, 0, 1, 4)
        mainLayout.setRowMinimumHeight(1, 6)
        mainLayout.addWidget(shapeLabel, 2, 1, Qt.AlignRight)
        mainLayout.addWidget(self.shapeComboBox, 2, 2)
        mainLayout.addWidget(penWidthLabel, 3, 1, Qt.AlignRight)
        mainLayout.addWidget(self.penWidthSpinBox, 3, 2)
        mainLayout.addWidget(penStyleLabel, 4, 1, Qt.AlignRight)
        mainLayout.addWidget(self.penStyleComboBox, 4, 2)
        mainLayout.addWidget(penCapLabel, 5, 1, Qt.AlignRight)
        mainLayout.addWidget(self.penCapComboBox, 5, 2)
        mainLayout.addWidget(penJoinLabel, 6, 1, Qt.AlignRight)
        mainLayout.addWidget(self.penJoinComboBox, 6, 2)
        mainLayout.addWidget(brushStyleLabel, 7, 1, Qt.AlignRight)
        mainLayout.addWidget(self.brushStyleComboBox, 7, 2)
        mainLayout.setRowMinimumHeight(8, 6)
        mainLayout.addWidget(otherOptionsLabel, 9, 1, Qt.AlignRight)
        mainLayout.addWidget(self.antialiasingCheckBox, 9, 2)
        mainLayout.addWidget(self.transformationsCheckBox, 10, 2)
        self.setLayout(mainLayout)

        self.shapeChanged()
        self.penChanged()
        self.brushChanged()
        self.antialiasingCheckBox.setChecked(True)

        self.setWindowTitle("Basic Drawing")

    def shapeChanged(self):
        shape = self.shapeComboBox.itemData(self.shapeComboBox.currentIndex(),
                                            IdRole)
        self.renderArea.setShape(shape)

    def penChanged(self):
        width = self.penWidthSpinBox.value()
        style = Qt.PenStyle(
            self.penStyleComboBox.itemData(
                self.penStyleComboBox.currentIndex(), IdRole))
        cap = Qt.PenCapStyle(
            self.penCapComboBox.itemData(self.penCapComboBox.currentIndex(),
                                         IdRole))
        join = Qt.PenJoinStyle(
            self.penJoinComboBox.itemData(self.penJoinComboBox.currentIndex(),
                                          IdRole))

        self.renderArea.setPen(QPen(Qt.blue, width, style, cap, join))

    def brushChanged(self):
        style = Qt.BrushStyle(
            self.brushStyleComboBox.itemData(
                self.brushStyleComboBox.currentIndex(), IdRole))

        if style == Qt.LinearGradientPattern:
            linearGradient = QLinearGradient(0, 0, 100, 100)
            linearGradient.setColorAt(0.0, Qt.white)
            linearGradient.setColorAt(0.2, Qt.green)
            linearGradient.setColorAt(1.0, Qt.black)
            self.renderArea.setBrush(QBrush(linearGradient))
        elif style == Qt.RadialGradientPattern:
            radialGradient = QRadialGradient(50, 50, 50, 70, 70)
            radialGradient.setColorAt(0.0, Qt.white)
            radialGradient.setColorAt(0.2, Qt.green)
            radialGradient.setColorAt(1.0, Qt.black)
            self.renderArea.setBrush(QBrush(radialGradient))
        elif style == Qt.ConicalGradientPattern:
            conicalGradient = QConicalGradient(50, 50, 150)
            conicalGradient.setColorAt(0.0, Qt.white)
            conicalGradient.setColorAt(0.2, Qt.green)
            conicalGradient.setColorAt(1.0, Qt.black)
            self.renderArea.setBrush(QBrush(conicalGradient))
        elif style == Qt.TexturePattern:
            self.renderArea.setBrush(QBrush(QPixmap(':/images/brick.png')))
        else:
            self.renderArea.setBrush(QBrush(Qt.green, style))
class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()

        self.proxyModel = SortFilterProxyModel()
        self.proxyModel.setDynamicSortFilter(True)

        self.sourceGroupBox = QGroupBox("Original Model")
        self.proxyGroupBox = QGroupBox("Sorted/Filtered Model")

        self.sourceView = QTreeView()
        self.sourceView.setRootIsDecorated(False)
        self.sourceView.setAlternatingRowColors(True)

        self.proxyView = QTreeView()
        self.proxyView.setRootIsDecorated(False)
        self.proxyView.setAlternatingRowColors(True)
        self.proxyView.setModel(self.proxyModel)
        self.proxyView.setSortingEnabled(True)

        self.sortCaseSensitivityCheckBox = QCheckBox("Case sensitive sorting")
        self.filterCaseSensitivityCheckBox = QCheckBox("Case sensitive filter")

        self.filterPatternLineEdit = QLineEdit()
        self.filterPatternLabel = QLabel("&Filter pattern:")
        self.filterPatternLabel.setBuddy(self.filterPatternLineEdit)

        self.filterSyntaxComboBox = QComboBox()
        self.filterSyntaxComboBox.addItem("Regular expression", QRegExp.RegExp)
        self.filterSyntaxComboBox.addItem("Wildcard", QRegExp.Wildcard)
        self.filterSyntaxComboBox.addItem("Fixed string", QRegExp.FixedString)
        self.filterSyntaxLabel = QLabel("Filter &syntax:")
        self.filterSyntaxLabel.setBuddy(self.filterSyntaxComboBox)

        self.filterColumnComboBox = QComboBox()
        self.filterColumnComboBox.addItem("Subject")
        self.filterColumnComboBox.addItem("Sender")
        self.filterColumnComboBox.addItem("Date")
        self.filterColumnLabel = QLabel("Filter &column:")
        self.filterColumnLabel.setBuddy(self.filterColumnComboBox)

        self.filterPatternLineEdit.textChanged.connect(self.filterRegExpChanged)
        self.filterSyntaxComboBox.currentIndexChanged.connect(self.filterRegExpChanged)
        self.filterColumnComboBox.currentIndexChanged.connect(self.filterColumnChanged)
        self.filterCaseSensitivityCheckBox.toggled.connect(self.filterRegExpChanged)
        self.sortCaseSensitivityCheckBox.toggled.connect(self.sortChanged)

        sourceLayout = QHBoxLayout()
        sourceLayout.addWidget(self.sourceView)
        self.sourceGroupBox.setLayout(sourceLayout)

        proxyLayout = QGridLayout()
        proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3)
        proxyLayout.addWidget(self.filterPatternLabel, 1, 0)
        proxyLayout.addWidget(self.filterPatternLineEdit, 1, 1, 1, 2)
        proxyLayout.addWidget(self.filterSyntaxLabel, 2, 0)
        proxyLayout.addWidget(self.filterSyntaxComboBox, 2, 1, 1, 2)
        proxyLayout.addWidget(self.filterColumnLabel, 3, 0)
        proxyLayout.addWidget(self.filterColumnComboBox, 3, 1, 1, 2)
        proxyLayout.addWidget(self.filterCaseSensitivityCheckBox, 4, 0, 1, 2)
        proxyLayout.addWidget(self.sortCaseSensitivityCheckBox, 4, 2)
        self.proxyGroupBox.setLayout(proxyLayout)

        mainLayout = QVBoxLayout()
        mainLayout.addWidget(self.sourceGroupBox)
        mainLayout.addWidget(self.proxyGroupBox)
        self.setLayout(mainLayout)

        self.setWindowTitle("Basic Sort/Filter Model")
        self.resize(500, 450)

        self.proxyView.sortByColumn(SENDER, Qt.AscendingOrder)
        self.filterColumnComboBox.setCurrentIndex(SENDER)

        self.filterPatternLineEdit.setText("Andy|Grace")
        self.filterCaseSensitivityCheckBox.setChecked(True)
        self.sortCaseSensitivityCheckBox.setChecked(True)

    def setSourceModel(self, model):
        self.proxyModel.setSourceModel(model)
        self.sourceView.setModel(model)

    def filterRegExpChanged(self):
        syntax_nr = self.filterSyntaxComboBox.itemData(self.filterSyntaxComboBox.currentIndex())
        syntax = QRegExp.PatternSyntax(syntax_nr)

        if self.filterCaseSensitivityCheckBox.isChecked():
            caseSensitivity = Qt.CaseSensitive
        else:
            caseSensitivity = Qt.CaseInsensitive

        regExp = QRegExp(self.filterPatternLineEdit.text(),
                caseSensitivity, syntax)
        self.proxyModel.setFilterRegExp(regExp)

    def filterColumnChanged(self):
        self.proxyModel.setFilterKeyColumn(self.filterColumnComboBox.currentIndex())

    def sortChanged(self):
        if self.sortCaseSensitivityCheckBox.isChecked():
            caseSensitivity = Qt.CaseSensitive
        else:
            caseSensitivity = Qt.CaseInsensitive

        self.proxyModel.setSortCaseSensitivity(caseSensitivity)
class Window(QWidget):
    def __init__(self):
        super(Window, self).__init__()

        self.proxyModel = MySortFilterProxyModel(self)
        self.proxyModel.setDynamicSortFilter(True)

        self.sourceView = QTreeView()
        self.sourceView.setRootIsDecorated(False)
        self.sourceView.setAlternatingRowColors(True)

        sourceLayout = QHBoxLayout()
        sourceLayout.addWidget(self.sourceView)
        sourceGroupBox = QGroupBox("Original Model")
        sourceGroupBox.setLayout(sourceLayout)

        self.filterCaseSensitivityCheckBox = QCheckBox("Case sensitive filter")
        self.filterCaseSensitivityCheckBox.setChecked(True)
        self.filterPatternLineEdit = QLineEdit()
        self.filterPatternLineEdit.setText("Grace|Sports")
        filterPatternLabel = QLabel("&Filter pattern:")
        filterPatternLabel.setBuddy(self.filterPatternLineEdit)
        self.filterSyntaxComboBox = QComboBox()
        self.filterSyntaxComboBox.addItem("Regular expression", QRegExp.RegExp)
        self.filterSyntaxComboBox.addItem("Wildcard", QRegExp.Wildcard)
        self.filterSyntaxComboBox.addItem("Fixed string", QRegExp.FixedString)
        self.fromDateEdit = QDateEdit()
        self.fromDateEdit.setDate(QDate(2006, 12, 22))
        self.fromDateEdit.setCalendarPopup(True)
        fromLabel = QLabel("F&rom:")
        fromLabel.setBuddy(self.fromDateEdit)
        self.toDateEdit = QDateEdit()
        self.toDateEdit.setDate(QDate(2007, 1, 5))
        self.toDateEdit.setCalendarPopup(True)
        toLabel = QLabel("&To:")
        toLabel.setBuddy(self.toDateEdit)

        self.filterPatternLineEdit.textChanged.connect(self.textFilterChanged)
        self.filterSyntaxComboBox.currentIndexChanged.connect(
            self.textFilterChanged)
        self.filterCaseSensitivityCheckBox.toggled.connect(
            self.textFilterChanged)
        self.fromDateEdit.dateChanged.connect(self.dateFilterChanged)
        self.toDateEdit.dateChanged.connect(self.dateFilterChanged)

        self.proxyView = QTreeView()
        self.proxyView.setRootIsDecorated(False)
        self.proxyView.setAlternatingRowColors(True)
        self.proxyView.setModel(self.proxyModel)
        self.proxyView.setSortingEnabled(True)
        self.proxyView.sortByColumn(1, Qt.AscendingOrder)

        self.textFilterChanged()
        self.dateFilterChanged()

        proxyLayout = QGridLayout()
        proxyLayout.addWidget(self.proxyView, 0, 0, 1, 3)
        proxyLayout.addWidget(filterPatternLabel, 1, 0)
        proxyLayout.addWidget(self.filterPatternLineEdit, 1, 1)
        proxyLayout.addWidget(self.filterSyntaxComboBox, 1, 2)
        proxyLayout.addWidget(self.filterCaseSensitivityCheckBox, 2, 0, 1, 3)
        proxyLayout.addWidget(fromLabel, 3, 0)
        proxyLayout.addWidget(self.fromDateEdit, 3, 1, 1, 2)
        proxyLayout.addWidget(toLabel, 4, 0)
        proxyLayout.addWidget(self.toDateEdit, 4, 1, 1, 2)
        proxyGroupBox = QGroupBox("Sorted/Filtered Model")
        proxyGroupBox.setLayout(proxyLayout)

        mainLayout = QVBoxLayout()
        mainLayout.addWidget(sourceGroupBox)
        mainLayout.addWidget(proxyGroupBox)
        self.setLayout(mainLayout)

        self.setWindowTitle("Custom Sort/Filter Model")
        self.resize(500, 450)

    def setSourceModel(self, model):
        self.proxyModel.setSourceModel(model)
        self.sourceView.setModel(model)

    def textFilterChanged(self):
        syntax = QRegExp.PatternSyntax(
            self.filterSyntaxComboBox.itemData(
                self.filterSyntaxComboBox.currentIndex()))
        caseSensitivity = (self.filterCaseSensitivityCheckBox.isChecked()
                           and Qt.CaseSensitive or Qt.CaseInsensitive)
        regExp = QRegExp(self.filterPatternLineEdit.text(), caseSensitivity,
                         syntax)
        self.proxyModel.setFilterRegExp(regExp)

    def dateFilterChanged(self):
        self.proxyModel.setFilterMinimumDate(self.fromDateEdit.date())
        self.proxyModel.setFilterMaximumDate(self.toDateEdit.date())
Exemple #56
0
class AnalogIn(PluginBase):
    def __init__(self, *args):
        super().__init__(BrickletAnalogIn, *args)

        self.ai = self.device

        # the firmware version of a EEPROM Bricklet can (under common circumstances)
        # not change during the lifetime of an EEPROM Bricklet plugin. therefore,
        # it's okay to make final decisions based on it here
        self.has_range = self.firmware_version >= (2, 0, 1)
        self.has_averaging = self.firmware_version >= (2, 0, 3)

        self.cbe_voltage = CallbackEmulator(self.ai.get_voltage,
                                            None,
                                            self.cb_voltage,
                                            self.increase_error_count)

        self.current_voltage = CurveValueWrapper() # float, V

        plots = [('Voltage', Qt.red, self.current_voltage, format_voltage)]
        self.plot_widget = PlotWidget('Voltage [V]', plots, y_resolution=0.001)

        layout = QVBoxLayout(self)
        layout.addWidget(self.plot_widget)

        if self.has_range:
            self.combo_range = QComboBox()
            self.combo_range.addItem('Automatic', BrickletAnalogIn.RANGE_AUTOMATIC)

            if self.has_averaging:
                self.combo_range.addItem('0 - 3.30 V', BrickletAnalogIn.RANGE_UP_TO_3V)

            self.combo_range.addItem('0 - 6.05 V', BrickletAnalogIn.RANGE_UP_TO_6V)
            self.combo_range.addItem('0 - 10.32 V', BrickletAnalogIn.RANGE_UP_TO_10V)
            self.combo_range.addItem('0 - 36.30 V', BrickletAnalogIn.RANGE_UP_TO_36V)
            self.combo_range.addItem('0 - 45.00 V', BrickletAnalogIn.RANGE_UP_TO_45V)
            self.combo_range.currentIndexChanged.connect(self.range_changed)

            hlayout = QHBoxLayout()
            hlayout.addWidget(QLabel('Range:'))
            hlayout.addWidget(self.combo_range)
            hlayout.addStretch()

            if self.has_averaging:
                self.spin_average = QSpinBox()
                self.spin_average.setMinimum(0)
                self.spin_average.setMaximum(255)
                self.spin_average.setSingleStep(1)
                self.spin_average.setValue(50)
                self.spin_average.editingFinished.connect(self.spin_average_finished)

                hlayout.addWidget(QLabel('Average Length:'))
                hlayout.addWidget(self.spin_average)

            line = QFrame()
            line.setObjectName("line")
            line.setFrameShape(QFrame.HLine)
            line.setFrameShadow(QFrame.Sunken)

            layout.addWidget(line)
            layout.addLayout(hlayout)

    def get_range_async(self, range_):
        self.combo_range.setCurrentIndex(self.combo_range.findData(range_))

    def get_averaging_async(self, average):
        self.spin_average.setValue(average)

    def start(self):
        if self.has_range:
            async_call(self.ai.get_range, None, self.get_range_async, self.increase_error_count)

        if self.has_averaging:
            async_call(self.ai.get_averaging, None, self.get_averaging_async, self.increase_error_count)

        self.cbe_voltage.set_period(100)

        self.plot_widget.stop = False

    def stop(self):
        self.cbe_voltage.set_period(0)

        self.plot_widget.stop = True

    def destroy(self):
        pass

    @staticmethod
    def has_device_identifier(device_identifier):
        return device_identifier == BrickletAnalogIn.DEVICE_IDENTIFIER

    def cb_voltage(self, voltage):
        self.current_voltage.value = voltage / 1000.0

    def range_changed(self, index):
        if index >= 0 and self.has_range:
            range_ = self.combo_range.itemData(index)
            async_call(self.ai.set_range, range_, None, self.increase_error_count)

    def spin_average_finished(self):
        self.ai.set_averaging(self.spin_average.value())
Exemple #57
0
class DeviceBar(QWidget):
    """ DeviceBar

        Signals:
            onDeviceUpdated()
            onDeviceSelected(str) # str = id
            onDeviceChanged(str) # str = id

    """

    onDeviceUpdated = pyqtSignal(str, name="onDeviceUpdated")
    onDeviceSelected = pyqtSignal(str, name="onDeviceSelected")
    onDeviceChanged = pyqtSignal(str, name="onDeviceChanged")

    def __init__(self, parent=None, device_type='usb'):
        super().__init__(parent=parent)

        # dont show for local
        if device_type != 'usb':
            return

        self.parent = parent
        self.wait_for_devtype = device_type
        self.is_waiting = True
        self._adb = Adb()

        if not self._adb.min_required:
            return

        self._git = Git()
        self.setAutoFillBackground(True)
        self.setStyleSheet('background-color: crimson; color: white; font-weight: bold; margin: 0; padding: 10px;')
        self.setup()
        self._timer = QTimer()
        self._timer.setInterval(500)
        self._timer.timeout.connect(self._on_timer)
        self._timer.start()
        self._timer_step = 0
        frida.get_device_manager().on('added', self._on_device)
        frida.get_device_manager().on('removed', self._on_device)
        self.devices_thread = DevicesUpdateThread(self)
        self.devices_thread.onAddDevice.connect(self.on_add_deviceitem)
        self.devices_thread.onDevicesUpdated.connect(self._on_devices_finished)
        self._update_thread = FridaUpdateThread(self)
        self._update_thread._adb = self._adb
        self._update_thread.onStatusUpdate.connect(self._update_statuslbl)
        self._update_thread.onFinished.connect(self._frida_updated)
        self._update_thread.onError.connect(self._on_download_error)
        self.updated_frida_version = ''
        self.updated_frida_assets_url = {}
        self._device_id = None
        self._devices = []
        remote_frida = self._git.get_frida_version()
        if remote_frida is None:
            self.updated_frida_version = ''
            self.updated_frida_assets_url.clear()
        else:
            remote_frida = remote_frida[0]
            self.updated_frida_version = remote_frida['tag_name']
            for asset in remote_frida['assets']:
                try:
                    name = asset['name']
                    tag_start = name.index('android-')
                    if name.index('server') >= 0:
                        tag = name[tag_start + 8:-3]
                        self.updated_frida_assets_url[tag] = asset['browser_download_url']
                except ValueError:
                    pass

    def setup(self):
        """ Setup ui
        """
        h_box = QHBoxLayout()
        h_box.setContentsMargins(0, 0, 0, 0)
        self.update_label = QLabel('Waiting for Device')
        self.update_label.setFixedWidth(self.parent.width())
        self.update_label.setOpenExternalLinks(True)
        self.update_label.setTextFormat(Qt.RichText)
        self.update_label.setFixedHeight(35)
        self.update_label.setTextInteractionFlags(Qt.TextBrowserInteraction)
        self._install_btn = QPushButton('Install Frida', self.update_label)
        self._install_btn.setStyleSheet('padding: 0; border-color: white;')
        self._install_btn.setGeometry(self.update_label.width() - 110, 5, 100, 25)
        self._install_btn.clicked.connect(self._on_install_btn)
        self._install_btn.setVisible(False)
        self._start_btn = QPushButton('Start Frida', self.update_label)
        self._start_btn.setStyleSheet('padding: 0; border-color: white;')
        self._start_btn.setGeometry(self.update_label.width() - 110, 5, 100, 25)
        self._start_btn.clicked.connect(self._on_start_btn)
        self._start_btn.setVisible(False)
        self._update_btn = QPushButton('Update Frida', self.update_label)
        self._update_btn.setStyleSheet('padding: 0; border-color: white;')
        self._update_btn.setGeometry(self.update_label.width() - 110, 5, 100, 25)
        self._update_btn.clicked.connect(self._on_install_btn)
        self._update_btn.setVisible(False)
        self._restart_btn = QPushButton('Restart Frida', self.update_label)
        self._restart_btn.setStyleSheet('padding: 0; border-color: white;')
        self._restart_btn.setGeometry(self.update_label.width() - 110, 5, 100, 25)
        self._restart_btn.clicked.connect(self._on_restart_btn)
        self._restart_btn.setVisible(False)
        self._devices_combobox = QComboBox(self.update_label)
        self._devices_combobox.setStyleSheet('padding: 2px 5px; border-color: white;')
        self._devices_combobox.setGeometry(self.update_label.width() - 320, 5, 200, 25)
        self._devices_combobox.currentIndexChanged.connect(self._on_device_changed)
        self._devices_combobox.setVisible(False)
        h_box.addWidget(self.update_label)
        self.setLayout(h_box)

    def on_add_deviceitem(self, device_ident):
        """ Adds an Item to the DeviceComboBox
        """
        if device_ident['type'] == self.wait_for_devtype:
            if device_ident['name'] not in self._devices:
                self._devices.append(device_ident)
            self._timer_step = -1
            self.is_waiting = False

    def _on_device_changed(self, index):
        device = None
        device_id = self._devices_combobox.itemData(index)
        if device_id:
            try:
                device = frida.get_device(device_id)
            except:
                return

            if device:
                self._device_id = device.id
                self._check_device(device)
                self.onDeviceChanged.emit(self._device_id)

    def _check_device(self, frida_device):
        self.update_label.setStyleSheet('background-color: crimson;')
        self._install_btn.setVisible(False)
        self._update_btn.setVisible(False)
        self._start_btn.setVisible(False)
        self._restart_btn.setVisible(False)
        self._adb.device = frida_device.id
        self._device_id = frida_device.id
        if self._adb.available():
            self.update_label.setText('Device: ' + frida_device.name)
            # try getting frida version
            device_frida = self._adb.get_frida_version()
            # frida not found show install button
            if device_frida is None:
                self._install_btn.setVisible(True)
            else:
                # frida is old show update button
                if self.updated_frida_version != device_frida:
                    self._start_btn.setVisible(True)
                    self._update_btn.setVisible(False)
                    # old frida is running allow use of this version
                    if self._adb.is_frida_running():
                        self._start_btn.setVisible(False)
                        if self.updated_frida_assets_url:
                            self._update_btn.setVisible(True)
                        self.update_label.setStyleSheet('background-color: yellowgreen;')
                        self.onDeviceUpdated.emit(frida_device.id)
                # frida not running show start button
                elif device_frida and not self._adb.is_frida_running():
                    self._start_btn.setVisible(True)
                # frida is running with last version show restart button
                elif device_frida and self._adb.is_frida_running():
                    self.update_label.setStyleSheet('background-color: yellowgreen;')
                    self._restart_btn.setVisible(True)
                    self.onDeviceUpdated.emit(frida_device.id)

        elif self._adb.non_root_available():
            self.update_label.setText('Device: ' + frida_device.name + ' (NOROOT!)')
            self.onDeviceUpdated.emit(frida_device.id)

    def _on_devices_finished(self):
        if self._devices:
            if len(self._devices) > 1:
                self._devices_combobox.clear()
                self._devices_combobox.setVisible(True)
                self.update_label.setText('Please select the Device: ')
                for device in self._devices:
                    self._devices_combobox.addItem(device['name'], device['id'])
            else:
                self._devices_combobox.setVisible(False)
                try:
                    device = frida.get_device(self._devices[0]['id'])
                    self._check_device(device)
                except:
                    pass

    def _on_timer(self):
        if self._timer_step == -1:
            self._timer.stop()
            return

        if self._timer_step == 0:
            self.update_label.setText(self.update_label.text() + ' .')
            self._timer_step = 1
        elif self._timer_step == 1:
            self.update_label.setText(self.update_label.text() + '.')
            self._timer_step = 2
        elif self._timer_step == 2:
            self.update_label.setText(self.update_label.text() + '.')
            self._timer_step = 3
        else:
            self.update_label.setText(self.update_label.text()[:-self._timer_step])
            self._timer_step = 0
            if self.is_waiting and self.devices_thread is not None:
                if not self.devices_thread.isRunning():
                    self.devices_thread.start()

    def _on_download_error(self, text):
        self._timer_step = -1
        self.update_label.setStyleSheet('background-color: crimson;')
        self.update_label.setText(text)
        self._install_btn.setVisible(True)
        self._update_btn.setVisible(False)

    def _on_device(self):
        self.update_label.setText('Waiting for Device ...')
        self._timer_step = 3
        self.is_waiting = True
        self._on_timer()

    def _on_install_btn(self):
        # urls are empty
        if not self.updated_frida_assets_url:
            return

        arch = self._adb.get_device_arch()
        request_url = ''

        if arch is not None and len(arch) > 1:
            arch = arch.join(arch.split())

            if arch == 'arm64' or arch == 'arm64-v8a':
                request_url = self.updated_frida_assets_url['arm64']
            elif arch == 'armeabi-v7a':
                request_url = self.updated_frida_assets_url['arm']
            else:
                if arch in self.updated_frida_assets_url:
                    request_url = self.updated_frida_assets_url[arch]

            try:
                if self._adb.available() and request_url.index('https://') == 0:
                    self._install_btn.setVisible(False)
                    self._update_btn.setVisible(False)
                    qApp.processEvents()
                    if self._update_thread is not None:
                        if not self._update_thread.isRunning():
                            self._update_thread.frida_update_url = request_url
                            self._update_thread.adb = self._adb
                            self._update_thread.start()

            except ValueError:
                # something wrong in .git_cache folder
                print("request_url not set")

    def _update_statuslbl(self, text):
        self._timer.stop()
        self._timer_step = 0
        self._timer.start()
        self.update_label.setText(text)

    def _frida_updated(self):
        #self._timer_step = 3
        #self.is_waiting = True
        self._on_devices_finished()

    def _on_start_btn(self):
        if self._adb.available():
            self._start_btn.setVisible(False)
            qApp.processEvents()
            if self._adb.start_frida():
                #self.onDeviceUpdated.emit(self._device_id)
                self._on_devices_finished()
            else:
                self._start_btn.setVisible(True)

    def _on_restart_btn(self):
        if self._adb.available():
            self._restart_btn.setVisible(False)
            qApp.processEvents()
            if self._adb.start_frida(restart=True):
                self._restart_btn.setVisible(True)
                #self.onDeviceUpdated.emit(self._device_id)
                self._on_devices_finished()
Exemple #58
0
class PlayerControls(QWidget):

    play = pyqtSignal()
    pause = pyqtSignal()
    stop = pyqtSignal()
    next = pyqtSignal()
    previous = pyqtSignal()
    changeVolume = pyqtSignal(int)
    changeMuting = pyqtSignal(bool)
    changeRate = pyqtSignal(float)

    def __init__(self, parent=None):
        super(PlayerControls, self).__init__(parent)

        self.playerState = QMediaPlayer.StoppedState
        self.playerMuted = False

        self.playButton = QToolButton(clicked=self.playClicked)
        self.playButton.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))

        self.stopButton = QToolButton(clicked=self.stop)
        self.stopButton.setIcon(self.style().standardIcon(QStyle.SP_MediaStop))
        self.stopButton.setEnabled(False)

        self.nextButton = QToolButton(clicked=self.next)
        self.nextButton.setIcon(
                self.style().standardIcon(QStyle.SP_MediaSkipForward))

        self.previousButton = QToolButton(clicked=self.previous)
        self.previousButton.setIcon(
                self.style().standardIcon(QStyle.SP_MediaSkipBackward))

        self.muteButton = QToolButton(clicked=self.muteClicked)
        self.muteButton.setIcon(
                self.style().standardIcon(QStyle.SP_MediaVolume))

        self.volumeSlider = QSlider(Qt.Horizontal,
                sliderMoved=self.changeVolume)
        self.volumeSlider.setRange(0, 100)

        self.rateBox = QComboBox(activated=self.updateRate)
        self.rateBox.addItem("0.5x", 0.5)
        self.rateBox.addItem("1.0x", 1.0)
        self.rateBox.addItem("2.0x", 2.0)
        self.rateBox.setCurrentIndex(1)

        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(self.stopButton)
        layout.addWidget(self.previousButton)
        layout.addWidget(self.playButton)
        layout.addWidget(self.nextButton)
        layout.addWidget(self.muteButton)
        layout.addWidget(self.volumeSlider)
        layout.addWidget(self.rateBox)
        self.setLayout(layout)

    def state(self):
        return self.playerState

    def setState(self,state):
        if state != self.playerState:
            self.playerState = state

            if state == QMediaPlayer.StoppedState:
                self.stopButton.setEnabled(False)
                self.playButton.setIcon(
                        self.style().standardIcon(QStyle.SP_MediaPlay))
            elif state == QMediaPlayer.PlayingState:
                self.stopButton.setEnabled(True)
                self.playButton.setIcon(
                        self.style().standardIcon(QStyle.SP_MediaPause))
            elif state == QMediaPlayer.PausedState:
                self.stopButton.setEnabled(True)
                self.playButton.setIcon(
                        self.style().standardIcon(QStyle.SP_MediaPlay))

    def volume(self):
        return self.volumeSlider.value()

    def setVolume(self, volume):
        self.volumeSlider.setValue(volume)

    def isMuted(self):
        return self.playerMuted

    def setMuted(self, muted):
        if muted != self.playerMuted:
            self.playerMuted = muted

            self.muteButton.setIcon(
                    self.style().standardIcon(
                            QStyle.SP_MediaVolumeMuted if muted else QStyle.SP_MediaVolume))

    def playClicked(self):
        if self.playerState in (QMediaPlayer.StoppedState, QMediaPlayer.PausedState):
            self.play.emit()
        elif self.playerState == QMediaPlayer.PlayingState:
            self.pause.emit()

    def muteClicked(self):
        self.changeMuting.emit(not self.playerMuted)

    def playbackRate(self):
        return self.rateBox.itemData(self.rateBox.currentIndex())

    def setPlaybackRate(self, rate):
        for i in range(self.rateBox.count()):
            if qFuzzyCompare(rate, self.rateBox.itemData(i)):
                self.rateBox.setCurrentIndex(i)
                return

        self.rateBox.addItem("%dx" % rate, rate)
        self.rateBox.setCurrentIndex(self.rateBox.count() - 1)

    def updateRate(self):
        self.changeRate.emit(self.playbackRate())
Exemple #59
0
class SearchWidget(QWidget, WidgetMixin):
	def __init__(self, **kwargs):
		super(SearchWidget, self).__init__(**kwargs)

		layout = QGridLayout()
		self.setLayout(layout)

		self.exprEdit = QLineEdit()
		self.exprEdit.returnPressed.connect(self.returnPressed)
		self.setFocusProxy(self.exprEdit)

		self.optionsButton = SearchOptionsButton()

		self.pluginChoice = QComboBox()
		plugins = sorted(file_search.enabledPlugins(), key=lambda p: p.name())
		for plugin in plugins:
			self.pluginChoice.addItem(plugin.name(), plugin.id)

		self.results = LocationList()
		self.results.setColumns(['path', 'line', 'snippet'])

		self.searcher = None

		layout.addWidget(self.exprEdit, 0, 0)
		layout.addWidget(self.optionsButton, 0, 1)
		layout.addWidget(self.pluginChoice, 0, 2)
		layout.addWidget(self.results, 1, 0, 1, -1)

		self.addCategory('file_search_widget')

	def setPlugin(self, id):
		index = self.pluginChoice.findData(id)
		if index >= 0:
			self.pluginChoice.setCurrentIndex(index)

	def setText(self, text):
		self.exprEdit.setText(text)

	def selectedPlugin(self):
		return self.pluginChoice.itemData(self.pluginChoice.currentIndex())

	def regexp(self):
		re = QRegExp(self.exprEdit.text())
		re.setCaseSensitivity(csToQtEnum(self.optionsButton.caseSensitive()))
		re.setPatternSyntax(self.optionsButton.reFormat())
		return re

	def shouldFindRoot(self):
		return self.optionsButton.shouldFindRoot()

	def makeArgs(self, plugin):
		ed = buffers.currentBuffer()

		if self.shouldFindRoot():
			path = plugin.searchRootPath(ed.path)
		else:
			path = os.path.dirname(ed.path)
		pattern = self.exprEdit.text()
		ci = self.optionsButton.caseSensitive()
		return (path, pattern, ci)

	@Slot()
	def doSearch(self):
		self.results.clear()
		plugin_type = file_search.getPlugin(self.selectedPlugin())
		self.searcher = plugin_type()
		file_search.setupLocationList(self.searcher, self.results)
		args = self.makeArgs(self.searcher)
		self.searcher.search(*args)

	returnPressed = Signal()