def __init__(self): super().__init__() self.setMaximumHeight(70) optLayout = QHBoxLayout() self.select = QPushButton('Select') self.start = QPushButton('Start Bid') self.cancel = QPushButton('Reset') self.save = QPushButton('Save') self.select.setStyleSheet(""" QPushButton{ background-color: rgb(70, 72, 74); border-radius: 3px; font: 14pt; } """) self.start.setStyleSheet(""" QPushButton { background-color: rgb(70, 72, 74); border-radius: 3px; color: Green; font-size: 16pt; } """) self.start.setEnabled(False) self.cancel.setStyleSheet(""" QPushButton { background-color: rgb(70, 72, 74); border-radius: 3px; color: rgb(153, 0, 0); font: 16pt; } """) self.cancel.setEnabled(False) self.save.setStyleSheet(""" QPushButton { background-color: rgb(70, 72, 74); border-radius: 3px; color: rgb(0, 0, 153); font: 14pt; } """) optLayout.addWidget(self.select) optLayout.addSpacing(300) optLayout.addWidget(self.start) optLayout.addWidget(self.cancel) optLayout.addSpacing(300) optLayout.addWidget(self.save) optLayout.sizeHint() self.setLayout(optLayout) self.show()
def initUI(self): # general settings self.setWindowTitle('DaisyLiteGUI v1.0') # main layout mainlayout = QHBoxLayout() # get widgets self.camerasection = CameraSection(self, self.camera) self.manualmovement = ManualMovementSection(self, self.camera, self.DD) # add widgets to main layout mainlayout.addWidget(self.camerasection) mainlayout.addWidget(self.manualmovement) # check if DD plugged in, disable manual movement section if so # and display warning if not self.DDconnected: self.manualmovement.setEnabled(False) warning_dialog = QMessageBox.warning( self, 'DaisyDriver Warning', 'Warning: No DaisyDriver Detected.', QMessageBox.Ok) # set mainlayout as widget layout self.setLayout(mainlayout) # set window geometry self.setFixedSize(mainlayout.sizeHint()) self.move(0, 0)
def rendersearchlist(): # mydic_list1 has all the search history. You can iterate with the keywords # for i in mydic_list1 for i in range(len(mydic_list1)): layout = QHBoxLayout() layout.setSizeConstraint(QLayout.SetMinimumSize) item = QListWidgetItem(mpg.listWidget_2) # SAAD DB DONE COMMENTED BELOW in main label = QLabel(str(i+1)+ ") " + mydic_list1[i] ) label.setStyleSheet("height:fit-content;font-size:12pt;font-family: Segoe UI;font-style: normal;font-weight:100") label.setWordWrap(True); layout.addWidget(label) widget = QWidget() widget.setStyleSheet("height:fit-content;width:100%"); widget.setLayout(layout); item.setSizeHint(layout.sizeHint()) mpg.listWidget_2.addItem(item) mpg.listWidget_2.setItemWidget(item,widget)
def addPattern(self, str=""): w = QWidget() l = QHBoxLayout() l.addWidget(QLabel("▹")) edit = QLineEdit(str) edit.textEdited.connect(lambda: Entries.setCurrentUnsaved()) l.addWidget(edit) l.addStretch() w.setLayout(l) item = QListWidgetItem() item.setSizeHint(l.sizeHint()) self.keywordList.addItem(item) self.keywordList.setItemWidget(item, w)
def __init__(self, parent=None): super(CoordApp, self).__init__(parent) self.setWindowTitle('Mouse coordinates') container = QWidget() layout = QHBoxLayout() container.setLayout(layout) self.setCentralWidget(container) self.label = CoordWidget() self.label.selectionChanged.connect(self._showSelectionInfo) layout.addWidget(self.label) help = QPushButton('&?') help.clicked.connect(self.showHelp) layout.addWidget(help) self.follower = MouseFollower() self.follower.mouseMoved.connect(self.label.showCoords) # disable grabbing here to avoid confusion self.follower.clickWhileGrabbed.connect(self.follower.toggleGrab) self.follower.clickWhileGrabbed.connect(self._showGrabInfo) layout.addWidget(self.follower) self.statusBar() # creates it self.resize(layout.sizeHint()) ## shortcuts self.copyAction = QAction(self) self.copyAction.setShortcut(QKeySequence(QKeySequence.Copy)) self.copyAction.triggered.connect(self.copyToClibpoard) self.addAction(self.copyAction) self.toggleGrabAction = QAction(self) self.toggleGrabAction.setShortcut(QKeySequence(Qt.Key_G)) self.toggleGrabAction.triggered.connect(self.follower.toggleGrab) self.toggleGrabAction.triggered.connect(self._showGrabInfo) self.addAction(self.toggleGrabAction) self.toggleFollowAction = QAction(self) self.toggleFollowAction.setShortcut(QKeySequence(Qt.Key_Space)) self.toggleFollowAction.triggered.connect(self.follower.toggleFollow) self.addAction(self.toggleFollowAction) self.toggleAlwaysOnTopAction = QAction(self) self.toggleAlwaysOnTopAction.setShortcut(QKeySequence(Qt.Key_A)) self.toggleAlwaysOnTopAction.triggered.connect(self.toggleAlwaysOnTop) self.addAction(self.toggleAlwaysOnTopAction)
def renderselllist(): for i in mydic_list: if (i["uploaded_by"] == cu and i["status"] == "For Sale"): # EVERYTHING HERE with i # pass # pass # for i in range(len(mydic_list)): layout = QHBoxLayout() layout.setSizeConstraint(QLayout.SetMinimumSize) print(i["short_description"]) print(i) item = QListWidgetItem(mpg.listWidget_3) # SAAD DB DONE COMMENTED label = QLabel( str(i + 1) + ") Title: " + i['short_description'] + "\n" + "Uploaded By: " + str(i['uploaded_by']) + "\n" + "Rating: " + str(i['rating']) + "/5") # label = QLabel(str(i+1)+ ") Title:" + i['short_description'] + "\n" + " Request By: " + i['requested_by'] +"\n" +" Rating: " + str(i['rating']) + "/5") label.setStyleSheet( "height:fit-content;font-size:12pt;font-family: Segoe UI;font-style: normal;font-weight:100" ) label.setWordWrap(True) # label2 = QLabel("No of comments " + str(len(i['comments'])) + '\nStatus: ' + i['status']) label2 = QLabel("Data Size: " + i['data_size'] + '\nStatus: ' + i['status']) label2.setStyleSheet( "height:fit-content;font-size:12pt;font-family: Segoe UI;text-align:right" ) label2.setAlignment(QtCore.Qt.AlignCenter) label2.setWordWrap(True) layout.addWidget(label) layout.addWidget(label2) widget = QWidget() widget.setStyleSheet("height:fit-content;width:100%") widget.setLayout(layout) item.setSizeHint(layout.sizeHint()) mpg.listWidget_3.addItem(item) mpg.listWidget_3.setItemWidget(item, widget)
def __init__(self, parent=None): super(Winform, self).__init__(parent) self.setWindowTitle("水平布局管理例子") # 水平布局按照从左到右的顺序进行添加按钮部件。 hlayout = QHBoxLayout() hlayout.addWidget(QPushButton(str(1))) hlayout.addWidget(QPushButton(str(2))) hlayout.addWidget(QPushButton(str(3))) btn4 = QPushButton(str(4)) hlayout.addWidget(btn4) hlayout.addWidget(QPushButton(str(5))) # hlayout.setContentsMargins(0,0,0,0) #设置控件间的间距 hlayout.setSpacing(0) self.setLayout(hlayout) print("btn4:", hlayout.sizeHint())
def renderListItem(self, i): layout = QHBoxLayout() layout.setSizeConstraint(QLayout.SetMinimumSize) item = QListWidgetItem(self.ItemListView) label = QLabel( str(i + 1) + ") " + item_list[i]['short_description'] + "\n" + "Uploaded By: " + str(item_list[i]['uploaded_by']) + "\n" + "Rating: " + str(item_list[i]['rating']) + "/5") label.setStyleSheet( "height:fit-content;font-size:12pt;font-style: normal;font-weight:100;" ) # label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum) label.setWordWrap(True) label2 = QLabel("Data Size: " + item_list[i]['data_size'] + '\nStatus: ' + item_list[i]['status']) label2.setStyleSheet( "height:fit-content;font-size:12pt;text-align:right;") # label2.setStyleSheet("color: white; background: red;,text-align:right;"); label2.setAlignment(QtCore.Qt.AlignCenter) # label2.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Ignored) # label2.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum) label2.setWordWrap(True) layout.addWidget(label) layout.addWidget(label2) widget = QWidget() widget.setStyleSheet("height:fit-content;,width:100%") widget.setLayout(layout) item.setSizeHint(layout.sizeHint()) self.ItemListView.addItem(item) self.ItemListView.setItemWidget(item, widget)
class Pager(QWidget): finished = pyqtSignal() changed = pyqtSignal(int) def __init__(self, parent=None): QWidget.__init__(self, parent=parent) lay = QVBoxLayout(self) self.stack = QStackedWidget() self.stack.currentChanged.connect(self.onChanged) self.nextButton = QPushButton("Next") self.nextButton.clicked.connect(self.onNext) self.finishButton = QPushButton("Finish") self.finishButton.clicked.connect(self.onFinish) self.previousButton = QPushButton("Previous") self.previousButton.clicked.connect(self.onPrevious) self.disableControls() self.btnLayout = QHBoxLayout() self.btnLayout.addWidget(self.previousButton) self.btnLayout.addWidget(self.nextButton) self.btnLayout.addWidget(self.finishButton) lay.addWidget(self.stack) lay.addLayout(self.btnLayout) # events @pyqtSlot() def onNext(self): index = self.stack.currentIndex() top = self.stack.count() - 1 index += 1 if index >= top: index = top self.stack.setCurrentIndex(index) @pyqtSlot() def onPrevious(self): index = self.stack.currentIndex() bottom = 0 index -= 1 if index <= bottom: index = bottom self.stack.setCurrentIndex(index) @pyqtSlot() def onFinish(self): self.disableFinish() self.disablePrevious() self.enableNext() self.finished.emit() self.stack.setCurrentIndex(0) @pyqtSlot(int) def onChanged(self, index): self.disableControls() self.changed.emit(index) widget = self.stack.widget(index) widget.onEnter() if widget.nextEnabled: self.enableNext() if widget.previousEnabled: self.enablePrevious() @pyqtSlot() def onPageFinished(self): self.disableControls() index = self.stack.currentIndex() top = self.stack.count() - 1 bottom = 0 self.enableNext() if index >= top: self.disableNext() self.enableFinish() self.enablePrevious() if index <= bottom: self.disablePrevious() # previous/next/finish controls def enableNext(self): self.nextButton.show() def enableFinish(self): self.finishButton.show() def enablePrevious(self): self.previousButton.show() def enableControls(self): self.enableNext() self.enableFinish() self.enablePrevious() def disableNext(self): self.nextButton.hide() def disableFinish(self): self.finishButton.hide() def disablePrevious(self): self.previousButton.hide() def disableControls(self): self.disableNext() self.disableFinish() self.disablePrevious() # for adding widgets and controlling page def addPage(self, pageWidget): pageWidget.setPager(self) self.stack.addWidget(pageWidget) pageWidget.finished.connect(self.onPageFinished) def getButtonHeight(self): return self.btnLayout.sizeHint().height() def clearPages(self): self.stack.clear() def setPageIndex(self, index): self.stack.setCurrentIndex(index)
class Calendar(QWidget): def __init__(self, parent=None, year=int(time.strftime('%Y', time.localtime(time.time()))), month=int(time.strftime('%m', time.localtime(time.time())))): super().__init__(parent) # Set Schedule Layout Components =============== self.scheduleLayout = QVBoxLayout() # ------------------------------- self.titleBox = QHBoxLayout() self.titleLabel = QLabel("title: ") self.titleLineEdit = QLineEdit() # ------------------------------- self.placeBox = QHBoxLayout() self.placeLabel = QLabel("place: ") self.placeLineEdit = QLineEdit() # ------------------------------- self.dateBox = QHBoxLayout() self.dateLabel = QLabel("time:") self.fromHour = QSpinBox() self.fromMin = QSpinBox() self.toHour = QSpinBox() self.toMin = QSpinBox() # ------------------------------- self.discription = QHBoxLayout() self.contentLabel = QLabel("content: ") self.content = QTextEdit() # ------------------------------- self.modifyBtn = Button("Modifying", self.modifying) # ============================================== self.displayCalendar = MyCalendar() self.startDay = 0 self.maxDay = 0 self.currentYear = year self.currentMonth = month self.currentDay = 0 self.firstClick = True self.displayCalendar.loadHoliday() self.today = time.localtime() if os.name == 'nt': self.fileRoot = ".\schedules\schedules.txt" else: self.fileRoot = "./schedules/schedules.txt" try: scheduleFile = open(self.fileRoot, "rb") self.displayCalendar.schedule = pickle.load(scheduleFile) print(self.displayCalendar.schedule) except EOFError: pass # main layout self.mainLayout = QHBoxLayout() # Left side Layout ================================ self.leftLayout = QVBoxLayout() # Stacked Widget Part ----------------------------- # Setting Stacked Widget(like a switching Tabs) self.setSchedule = QWidget() self.lunaDate = QWidget() # Design And Setting Actions each Tab. if want to Append any action, plz input the action in here. self.setScheduleUI() self.lunaDateUI() # Appending tabs in Stack self.Stack = QStackedWidget() self.Stack.addWidget(self.setSchedule) self.Stack.addWidget(self.lunaDate) # Switching Button layout Design And binding button with action. self.tabLayout = QHBoxLayout() self.tabLayout.addWidget(Button("스케쥴러", lambda: self.display(0))) self.tabLayout.addWidget(Button("음력", lambda: self.display(1))) for i in range(self.tabLayout.count()): self.tabLayout.itemAt(i).widget().setStyleSheet('font-size: 8pt') self.leftLayout.addLayout(self.tabLayout) # ------------------------------------------------- # handling Year & month ---------------------------------- self.moveMonth = QHBoxLayout() self.previousBtn = Button("<", self.previousMonth) # showing Year and month using Combobox(Year range: 1980 ~ 2040, Month range: 1, 12) self.yearCombo = QComboBox() self.yearCombo.addItems([str(x) for x in range(1980, 2041)]) self.yearCombo.setCurrentText(str(self.currentYear)) self.monthCombo = QComboBox() self.monthCombo.addItems([str(x) for x in range(1, 13)]) self.monthCombo.setCurrentText(str(self.currentMonth)) self.nextBtn = Button(">", self.nextMonth) self.moveMonth.addStretch() self.moveMonth.addWidget(self.previousBtn) self.moveMonth.addWidget(self.yearCombo) self.moveMonth.addWidget(self.monthCombo) self.moveMonth.addWidget(self.nextBtn) self.moveMonth.addStretch() self.leftLayout.addLayout(self.moveMonth) self.leftLayout.addStretch() # ------------------------------------------------- # Set Day of Week --------------------------------- self.weekDayLayout = QHBoxLayout() enumDays = ["일", "월", "화", "수", "목", "금", "토"] for i in enumDays: label = QLabel(i) label.setAlignment(Qt.AlignCenter) self.weekDayLayout.addWidget(label) self.leftLayout.addLayout(self.weekDayLayout) # ------------------------------------------------- # grid layout to appending date Buttons self.calendarGrid = QGridLayout() self.calendarGrid.setSizeConstraint(QLayout.SetFixedSize) self.leftLayout.addLayout(self.calendarGrid) self.leftLayout.addStretch(7) # showing status self.statusLabel = QLabel("btn Status") self.leftLayout.addWidget(self.statusLabel) # ================================================== # Set grid self.displayCalendar.setCalander(self.currentYear, self.currentMonth) self.renderDate(self.displayCalendar.getCalander()) # Set ComboBox Changing Event self.yearCombo.currentTextChanged.connect( lambda: self.selectionChanged()) self.monthCombo.currentTextChanged.connect( lambda: self.selectionChanged()) self.mainLayout.addLayout(self.leftLayout) self.mainLayout.addWidget(self.Stack) self.Stack.setCurrentIndex(0) # default Tab -> set calendar self.setLayout(self.mainLayout) self.setWindowTitle("Calendar") def renderDate(self, newCalendar): # =========== Append Day Buttons =============== self.clearLayout(self.calendarGrid) todayYear = self.today.tm_year todayMonth = self.today.tm_mon todayDay = self.today.tm_mday toggle = True for i in newCalendar: print(i) # Enroll button for row, column in enumerate(newCalendar): for col, day in enumerate(column): btn = Button(str(day), self.btnEvent) # deactivate button condition if toggle: if day != 1: btn.setEnabled(False) else: toggle = False else: if (row == len(newCalendar) - 1) and (day // 10 == 0): btn.setEnabled(False) # set today button color if (self.currentYear, self.currentMonth, day) == (todayYear, todayMonth, todayDay): btn.setStyleSheet('font-style: italic;') # if this day have any event represent event key = '-'.join( [str(self.currentYear), str(self.currentMonth), str(day)]) if key in self.displayCalendar.schedule.keys( ) and btn.isEnabled(): btn.setStyleSheet('color: blue;') btn.setStyleSheet('background-color: skyblue;') btn.setToolTip( self.displayCalendar.schedule[key].getTitle()) for restMonth, restDay, title in self.displayCalendar.holidays: if restMonth == self.currentMonth and restDay == day and btn.isEnabled( ): if key in self.displayCalendar.schedule.keys( ) and btn.isEnabled(): btn.setStyleSheet( 'background-color: skyblue; color: red;') btn.setToolTip(title) else: btn.setStyleSheet('color: red;') btn.setToolTip(title) break # 공휴일은 빨간색으로 설정해준다. if col == 0 and btn.isEnabled(): btn.setStyleSheet('color: red;') self.calendarGrid.addWidget(btn, row, col) # =============================================== self.displayCalendar.enrollHoliday(self.currentYear) self.displayCalendar.loadHoliday() def btnEvent(self): # self.showingWidget(self.scheduleLayout) self.setFixedSize(self.mainLayout.sizeHint()) btn = self.sender() self.statusLabel.setText("Day: " + btn.text() + " is Clicked.") self.currentDay = btn.text() target = "-".join([ str(self.currentYear), str(self.currentMonth), str(self.currentDay) ]) targetEvent = self.displayCalendar.schedule.get(target) if not targetEvent: self.titleLineEdit.setText("None") self.placeLineEdit.clear() self.fromHour.setValue(0) self.fromMin.setValue(0) self.toHour.setValue(0) self.toMin.setValue(0) self.content.clear() else: self.titleLineEdit.setText(targetEvent.getTitle()) self.placeLineEdit.setText(targetEvent.getPlace()) timeSet = targetEvent.getDate().split(",") self.fromHour.setValue(int(timeSet[0])) self.fromMin.setValue(int(timeSet[1])) self.toHour.setValue(int(timeSet[2])) self.toMin.setValue(int(timeSet[3])) self.content.setText(targetEvent.getDescription()) def modifying(self): newEvent = MyEvent() eventList = [ self.titleLineEdit.text(), self.placeLineEdit.text(), ",".join([ str(self.fromHour.value()), str(self.fromMin.value()), str(self.toHour.value()), str(self.toMin.value()) ]), self.content.toPlainText(), ] newEvent.setEvent(*eventList) target = "-".join([ str(self.currentYear), str(self.currentMonth), str(self.currentDay) ]) self.displayCalendar.schedule[target] = newEvent self.statusLabel.setText("modified") # rendering previous month calendar def previousMonth(self): if self.currentMonth is 1: self.currentYear -= 1 self.yearCombo.setCurrentText(str(self.currentYear)) self.currentMonth = 12 self.monthCombo.setCurrentText(str(self.currentMonth)) else: self.currentMonth -= 1 self.monthCombo.setCurrentText(str(self.currentMonth)) # rendering next month calendar def nextMonth(self): if self.currentMonth is 12: self.currentYear += 1 self.yearCombo.setCurrentText(str(self.currentYear)) self.currentMonth = 1 self.monthCombo.setCurrentText(str(self.currentMonth)) else: self.currentMonth += 1 self.monthCombo.setCurrentText(str(self.currentMonth)) def selectionChanged(self): self.currentYear = int(self.yearCombo.currentText()) self.currentMonth = int(self.monthCombo.currentText()) self.displayCalendar.setYear(self.currentYear) self.displayCalendar.setMonth(self.currentMonth) self.displayCalendar.setCalander(self.currentYear, self.currentMonth) self.renderDate(self.displayCalendar.getCalander()) def clearLayout(self, layout): while layout.count(): child = layout.takeAt(0) if child.widget(): child.widget().deleteLater() def closeEvent(self, event): keys = [] myEvent = self.displayCalendar.schedule for target in myEvent.keys(): title = myEvent[target].title place = myEvent[target].place description = myEvent[target].description if (title, place, description) == ('', '', ''): keys.append(target) for target in keys: del self.displayCalendar.schedule[target] with open(self.fileRoot, "wb") as file: pickle.dump(self.displayCalendar.schedule, file) def setScheduleUI(self): # Schedules layout ================================== self.titleBox.addWidget(self.titleLabel) self.titleBox.addWidget(self.titleLineEdit) self.placeBox.addWidget(self.placeLabel) self.placeBox.addWidget(self.placeLineEdit) self.fromHour.setRange(0, 24) self.toHour.setRange(0, 24) self.fromMin.setRange(0, 59) self.toMin.setRange(0, 59) self.fromHour.valueChanged.connect( lambda: self.toHour.setRange(self.fromHour.value(), 24)) # self.toHour.valueChanged.connect(lambda: self.fromHour.setRange(0, self.toHour.value())) self.fromMin.valueChanged.connect( lambda: self.toMin.setRange(self.fromMin.value(), 59)) # self.toMin.valueChanged.connect(lambda: self.fromMin.setRange(0, self.toMin.value())) self.dateBox.addWidget(self.dateLabel) self.dateBox.addWidget(self.fromHour) self.dateBox.addWidget(self.fromMin) self.dateBox.addWidget(QLabel(" ~ ")) self.dateBox.addWidget(self.toHour) self.dateBox.addWidget(self.toMin) self.contentLabel.setAlignment(Qt.AlignTop) self.discription.addWidget(self.contentLabel) self.discription.addWidget(self.content) self.scheduleLayout.addLayout(self.titleBox) self.scheduleLayout.addLayout(self.placeBox) self.scheduleLayout.addLayout(self.dateBox) self.scheduleLayout.addLayout(self.discription) # modifying schedule Button self.scheduleLayout.addWidget(self.modifyBtn) self.setSchedule.setLayout(self.scheduleLayout) def lunaDateUI(self): month_31 = [1, 3, 5, 7, 8, 10, 12] layout = QVBoxLayout() #layout.addWidget(QLabel("Luna Date")) topLayout = QHBoxLayout() self.yearSpinner = QSpinBox() self.yearSpinner.setRange(1980, 2040) self.monthSpinner = QSpinBox() self.monthSpinner.setRange(1, 12) self.daySpinner = QSpinBox() self.yearSpinner.setValue(self.today.tm_year) self.monthSpinner.setValue(self.today.tm_mon) self.daySpinner.setValue(self.today.tm_mday) self.monthSpinner.valueChanged.connect( lambda: self.daySpinner.setRange( 1, self.displayCalendar.getMaxday(self.yearSpinner.value(), self.monthSpinner.value()))) self.modeComboBox = QComboBox() self.modeComboBox.addItems(["양력 -> 음력", "음력 -> 양력"]) convertBtn = Button("convert", self.lunarBtnEvent) resetBtn = Button("reset", self.lunarBtnEvent) bottomLayout = QVBoxLayout() titleBox = QHBoxLayout() solarBox = QHBoxLayout() lunarBox = QHBoxLayout() self.todayLabel = QLabel("오늘의 날짜정보") self.todayLabel.setStyleSheet('color: red; font-size: 18px;') solarLabel = QLabel("양력날짜") solarLabel.setStyleSheet('color: gray;') todaySolarDay = "%04d-%02d-%02d" % ( self.today.tm_year, self.today.tm_mon, self.today.tm_mday) self.solarDateLabel = QLabel(todaySolarDay) lunarLabel = QLabel("음력날짜") lunarLabel.setStyleSheet('color: gray;') self.lunarDateLabel = QLabel( self.displayCalendar.calculator.getToLunarDate( self.today.tm_year, self.today.tm_mon, self.today.tm_mday)) titleBox.addWidget(self.todayLabel) solarBox.addWidget(solarLabel) solarBox.addWidget(self.solarDateLabel) lunarBox.addWidget(lunarLabel) lunarBox.addWidget(self.lunarDateLabel) bottomLayout.addLayout(titleBox) bottomLayout.addLayout(solarBox) bottomLayout.addLayout(lunarBox) topLayout.addWidget(self.yearSpinner) topLayout.addWidget(self.monthSpinner) topLayout.addWidget(self.daySpinner) topLayout.addWidget(self.modeComboBox) topLayout.addWidget(convertBtn) topLayout.addWidget(resetBtn) layout.addStretch() layout.addLayout(bottomLayout) layout.addLayout(topLayout) layout.addStretch() self.lunaDate.setLayout(layout) def display(self, i): self.Stack.setCurrentIndex(i) def hidingWidget(self, layout): for i in range(layout.count()): item = layout.itemAt(i) if item.widget() is not None: layout.itemAt(i).widget().hide() elif item.layout() is not None: self.hidingWidget(layout.itemAt(i).layout()) def showingWidget(self, layout): for i in range(layout.count()): item = layout.itemAt(i) if item.widget() is not None: layout.itemAt(i).widget().show() elif item.layout() is not None: self.showingWidget(layout.itemAt(i).layout()) def lunarBtnEvent(self): btn = self.sender() key = btn.text() if key == 'reset': self.todayLabel.setText('오늘의 날짜정보') self.todayLabel.setStyleSheet('color: red; font-size: 18px;') elif key == 'convert': if self.modeComboBox.currentIndex() is 0: self.todayLabel.setText("양력 {}년 {}월 {}일".format( self.yearSpinner.value(), self.monthSpinner.value(), self.daySpinner.value())) self.todayLabel.setStyleSheet( 'font-weight: bold; color: black; font-size: 12px;') lunarDate = self.displayCalendar.calculator.getToLunarDate( self.yearSpinner.value(), self.monthSpinner.value(), self.daySpinner.value()) self.solarDateLabel.setText( str(self.yearSpinner.value()) + "-" + str(self.monthSpinner.value()) + "-" + str(self.daySpinner.value())) self.lunarDateLabel.setText(lunarDate) else: self.todayLabel.setText("음력 {}년 {}월 {}일".format( self.yearSpinner.value(), self.monthSpinner.value(), self.daySpinner.value())) self.todayLabel.setStyleSheet( 'font-weight: bold; color: black; font-size: 12px;') solarDate = self.displayCalendar.calculator.toSolarDate( self.yearSpinner.value(), self.monthSpinner.value(), self.daySpinner.value()) self.solarDateLabel.setText( str(solarDate[0]) + "-" + str(solarDate[1]) + "-" + str(solarDate[2])) self.lunarDateLabel.setText( str(self.yearSpinner.value()) + "-" + str(self.monthSpinner.value()) + "-" + str(self.daySpinner.value()))
class Appication(QMainWindow): def __init__(self): super().__init__() self.setWindowTitle(APP_TITLE) self.initUI() def initUI(self): self.init_menu() self.upload_file_label = QLabel('Upload data file:') self.upload_file_label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.upload_file_label.setAlignment(Qt.AlignTop) self.upload_file_label.setContentsMargins(0, 7, 0, 0) self.choose_data_button = QPushButton('Choose file') self.choose_data_button.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.choose_data_button.clicked.connect(self.show_files_dialog) self.data_fname_label = QLabel('No file chosen') self.data_fname_label.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Minimum) self.data_fname_label.setAlignment(Qt.AlignTop) self.data_fname_label.setContentsMargins(0, 7, 0, 0) self.classify_button = QPushButton('Classify') self.classify_button.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed) self.classify_button.setEnabled(False) self.classify_button.clicked.connect(self.plot_results) self.buttons_vlayout = QVBoxLayout() self.buttons_vlayout.addWidget(self.choose_data_button) self.buttons_vlayout.addWidget(self.classify_button) self.pred_hlayout = QHBoxLayout() self.pred_hlayout.setAlignment(Qt.AlignTop) self.pred_hlayout.addWidget(self.upload_file_label) self.pred_hlayout.addLayout(self.buttons_vlayout) self.pred_hlayout.addWidget(self.data_fname_label) self.canvas = PlotCanvas() self.nav_toolbar = NavigationToolbar(self.canvas, self) self.addToolBar(QtCore.Qt.BottomToolBarArea, self.nav_toolbar) self.scroll_area = QScrollArea() self.scroll_area.setAlignment(Qt.AlignTop) self.scroll_area.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Maximum) self.scroll_area.setFixedHeight( int(FIG_H * DPI) + self.scroll_area.horizontalScrollBar().sizeHint().height() + 2) self.scroll_area.setWidget(self.canvas) self.stat_results_group = QGroupBox('Classification results') self.init_stat_results() self.stat_results_group.setLayout(self.stat_results_layout) self.main_layout = QVBoxLayout() self.main_layout.setAlignment(Qt.AlignTop) self.main_layout.addLayout(self.pred_hlayout) self.main_layout.addWidget(self.scroll_area) self.main_layout.addWidget(self.stat_results_group) self.central_widget = QWidget() self.central_widget.setLayout(self.main_layout) self.setCentralWidget(self.central_widget) self.statusBar() self.showMaximized() def init_stat_results(self): self.stat_results_layout = QGridLayout() self.stat_results_layout.setAlignment(Qt.AlignLeft | Qt.AlignTop) self.stat_results_labels = { 'nor': QLabel('NOR - normal beats:'), 'pab': QLabel('PAB - paced beats:'), 'vfw': QLabel('VFW - ventricular flutter wave beats:'), 'veb': QLabel('VEB - ventricular escape beats:'), 'rbb': QLabel('RBB - right bundle branch block beats:'), 'lbb': QLabel('LBB - left bundle branch block beats:'), 'pvc': QLabel('PVC - premature ventricular contraction beats:'), 'apc': QLabel('APC - atrial premature contraction beats:') } self.stat_results_counts = dict( zip(self.stat_results_labels.keys(), [ QLabel('-') for i in range(len(self.stat_results_labels.keys())) ])) ind = 0 for c, label in self.stat_results_labels.items(): self.stat_results_labels[c].setSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed) self.stat_results_layout.addWidget(label, ind % 4, ind // 4 * 2, Qt.AlignRight) self.stat_results_counts[c].setContentsMargins(0, 0, 120, 0) self.stat_results_layout.addWidget(self.stat_results_counts[c], ind % 4, (ind // 4 * 2) + 1) ind += 1 def resume_stat_results(self): ind = 0 for c, _ in self.stat_results_labels.items(): self.stat_results_counts[c].setText('-') ind += 1 def set_stat_results(self, pred_classes): classes, counts = np.unique(pred_classes, return_counts=True) for i, c in enumerate(classes): self.stat_results_counts[c].setText(str(counts[i])) def init_menu(self): self.conf_action = QAction('&Configure', self) self.conf_action.triggered.connect(self.show_configure) self.main_menu = self.menuBar() self.settings_menu = self.main_menu.addMenu('&Settings') self.settings_menu.addAction(self.conf_action) def show_files_dialog(self): self.data_fname = QFileDialog.getOpenFileName( self, 'Choose data file', filter="ECG format files " "(*.txt *.ecg *.cmp *.ano *.edf *.hea *.atr *.dat)")[0] if self.data_fname != '': self.data_fname_label.setText( self.data_fname[self.data_fname.rfind('/') + 1:]) self.classify_button.setEnabled(self.data_fname != '') def define_analysis(self): global analysis_obj if analysis_obj is None: analysis_obj = analysis.CNNAnalysis(self.statusBar()) self.statusBar().showMessage('Model loaded, ready to classify') def plot_results(self): self.classify_button.setEnabled(False) self.resume_stat_results() self.statusBar().showMessage('Predicting...') analysis_res = analysis_obj.analyze(self.data_fname) if analysis_res is None: self.statusBar().showMessage('Error occured, ready to classify') return pred_classes, signals, fields, qrs_inds = analysis_res self.canvas = PlotCanvas(width=len(signals) // 120, height=FIG_H * signals.shape[1]) self.canvas.plot_analysis_res(signals, fields, pred_classes, qrs_inds) self.removeToolBar(self.nav_toolbar) self.nav_toolbar = NavigationToolbar(self.canvas, self) self.addToolBar(QtCore.Qt.BottomToolBarArea, self.nav_toolbar) self.scroll_area.setWidget(self.canvas) self.scroll_area.setFixedHeight(min( self.height() - self.statusBar().height() - \ self.nav_toolbar.sizeHint().height() - self.stat_results_layout.sizeHint().height() - \ self.pred_hlayout.sizeHint().height() - self.menuBar().height() - \ QApplication.style().pixelMetric(QStyle.PM_TitleBarHeight) - 25, int(FIG_H * DPI * signals.shape[1]) + self.scroll_area.horizontalScrollBar().sizeHint().height() + 2)) self.set_stat_results(pred_classes) self.statusBar().showMessage('Predicting completed, ready to classify') def show_stat_results(self): pass @staticmethod def show_configure(): ConfDialog()
class ledStrip(QWidget): """ [description] **Arguments:** None **Keword Arguments:** None **Author:** Chris Bruce, [email protected], 12/3/2018 """ TITLE = "LED Strip Simluation" VERSION = 1.0 SIZE = [300, 400] PIXEL_MIN = 8 PIXEL_MAX = 256 PIXEL_NUM = 32 PIXEL_SIZE_MIN = 8 PIXEL_SIZE_MAX = 64 STRIP_COLOR = const.grey # default RGB value for the strip PIXEL_COLOR = Color(12, 12, 12) # default RGB value for LED pixel #STYLE_SHEET = "QWidget{ background-color: rgb{} };".format( str( STRIP_COLOR ) ) STYLE_SHEET = "QWidget{ background-color:white};" def __init__(self, app): super(ledStrip, self).__init__() self.app = app self.pixels = [] # array of LED pixels self.pixel_size = 32 # size of each LED pixel self.pixel_space = 16 # size of space between each LED pixel self.paused = False self.testing = False self._init_gui() def _init_gui(self): """ [description] **Arguments:** None **Keword Arguments:** None **Author:** Chris Bruce, [email protected], 12/3/2018 """ self.setWindowTitle('{} - ver. {}'.format(self.TITLE, self.VERSION)) self.main_layout = QVBoxLayout() self.main_layout.setSpacing(self.pixel_space) self.setLayout(self.main_layout) self.setAutoFillBackground(True) self._strip_color() # size_policy = QSizePolicy( ) # size_policy.setHorizontalPolicy( QSizePolicy.Minimum ) # self.setSizePolicy( size_policy ) self.main_layout.addWidget(self._create_strip()) self.main_layout.addWidget(self._create_controls()) self._update_gui() def _create_controls(self): """ [description] **Arguments:** None **Keword Arguments:** None **Returns:** :``[type]``: [description] **Author:** Chris Bruce, [email protected], 12/3/2018 """ widget = QWidget() #QGroupBox( 'Settings:' ) layout = QHBoxLayout() widget.setLayout(layout) label_num = QLabel('LEDs:') spin_num = QSpinBox() spin_num.setRange(self.PIXEL_MIN, self.PIXEL_MAX) spin_num.setFixedWidth(64) spin_num.setValue(self.PIXEL_NUM) spin_num.valueChanged.connect(self._on_spin_num_changed) label_size = QLabel('Size:') spin_size = QSpinBox() spin_size.setRange(self.PIXEL_SIZE_MIN, self.PIXEL_SIZE_MAX) spin_size.setFixedWidth(64) spin_size.setValue(self.pixel_size) spin_size.valueChanged.connect(self._on_spin_size_changed) label_space = QLabel('Space:') spin_space = QSpinBox() spin_space.setRange(0, 256) spin_space.setFixedWidth(64) spin_space.setValue(self.pixel_space) spin_space.valueChanged.connect(self._on_spin_space_changed) self.tests = QComboBox() for test in sorted(tests.keys()): self.tests.addItem(test) # self.tests.setCurrentText( 'Soft Fade' ) btn_start = QPushButton('Start') btn_start.pressed.connect(self._on_btn_start_pressed) btn_pause = QPushButton('Pause') btn_pause.pressed.connect(self._on_btn_pause_pressed) btn_stop = QPushButton('Stop') btn_stop.pressed.connect(self._on_btn_stop_pressed) layout.addStretch() layout.addWidget(label_num) layout.addWidget(spin_num) layout.addWidget(label_size) layout.addWidget(spin_size) layout.addWidget(label_space) layout.addWidget(spin_space) layout.addWidget(self.tests) layout.addWidget(btn_start) layout.addWidget(btn_pause) layout.addWidget(btn_stop) layout.addStretch() return widget def _create_pixel(self, color): """ [description] **Arguments:** :``color``: `[type]` [description] **Keword Arguments:** None **Returns:** :``[type]``: [description] **Author:** Chris Bruce, [email protected], 12/3/2018 """ pixel = QFrame() pixel.setFixedSize(QSize(self.pixel_size, self.pixel_size)) pixel.setFrameShape(QFrame.Box) pixel.setFrameShadow(QFrame.Raised) pixel.setLineWidth(0) pixel.setMidLineWidth(self.pixel_size / 8) pixel.setStyleSheet("background-color: rgb{}".format(color)) return pixel def _create_strip(self): """ [description] **Arguments:** None **Keword Arguments:** None **Returns:** :``[type]``: [description] **Author:** Chris Bruce, [email protected], 12/3/2018 """ self.strip_widget = QWidget() self.strip_layout = QHBoxLayout() self.strip_layout.setSpacing(self.pixel_space) self.strip_widget.setLayout(self.strip_layout) for i in range(self.PIXEL_NUM): pixel = self._create_pixel(self.PIXEL_COLOR) self.pixels.append(pixel) self.strip_layout.addWidget(pixel) return self.strip_widget def _strip_color(self, color=STRIP_COLOR): """ Change the color of the strip **Arguments:** None **Keword Arguments:** None **Returns:** :``[type]``: [description] **Author:** Chris Bruce, [email protected], 12/3/2018 """ p = self.palette() p.setColor(self.backgroundRole(), QColor(*color)) self.setPalette(p) return color def _update_size(self): self.strip_widget.setFixedSize(self.strip_layout.sizeHint()) self.setFixedSize(self.main_layout.sizeHint()) self._center() return True def _center(self): qtRectangle = self.frameGeometry() centerPoint = QDesktopWidget().availableGeometry().center() qtRectangle.moveCenter(centerPoint) self.move(qtRectangle.topLeft()) return qtRectangle.topLeft() def _update_gui(self): self._update_size() self._center() return True def _on_spin_num_changed(self, value): """ Change the color of the strip **Arguments:** None **Keword Arguments:** None **Returns:** :``[type]``: [description] **Author:** Chris Bruce, [email protected], 12/3/2018 """ num_pixels = self.numPixels() if value > num_pixels: for _ in range(value - num_pixels): pixel = self._create_pixel(self.PIXEL_COLOR) self.pixels.append(pixel) self.strip_layout.addWidget(pixel) elif value < len(self.pixels): for _ in range(num_pixels - value): pixel = self.pixels[-1] self.strip_layout.removeWidget(pixel) self.pixels.pop() pixel.deleteLater() self._update_gui() def _on_spin_size_changed(self, value): """ Change the color of the strip **Arguments:** None **Keword Arguments:** None **Returns:** :``[type]``: [description] **Author:** Chris Bruce, [email protected], 12/3/2018 """ for pixel in self.pixels: self.pixel_size = value pixel.setFixedSize(QSize(self.pixel_size, self.pixel_size)) pixel.setMidLineWidth(self.pixel_size / 8) self._update_gui() def _on_spin_space_changed(self, value): """ Change the color of the strip **Arguments:** None **Keword Arguments:** None **Returns:** :``[type]``: [description] **Author:** Chris Bruce, [email protected], 12/3/2018 """ self.pixel_space = value self.strip_layout.setSpacing(self.pixel_space) self._update_gui() def _on_btn_start_pressed(self): self.testing = True sel_test = self.tests.currentText() test_func = tests[sel_test] return test_func(self) def _on_btn_pause_pressed(self): self.paused = not self.paused return True def _on_btn_stop_pressed(self): self.testing = False return True def numPixels(self): """ [description] **Arguments:** None **Keword Arguments:** None **Returns:** :``[type]``: [description] **Author:** Chris Bruce, [email protected], 12/3/2018 """ return len(self.pixels) def setPixelColor(self, i, color): """ [description] **Arguments:** :``i``: `[type]` [description] :``color``: `[type]` [description] **Keword Arguments:** None **Returns:** :``[type]``: [description] **Author:** Chris Bruce, [email protected], 12/3/2018 """ if color == 0: color = self.PIXEL_COLOR if i > (self.numPixels() - 1): i -= self.numPixels() pixel = self.pixels[i] pixel.setStyleSheet("background-color: rgb{}".format(color)) return True def show(self): """ Extends QWidget's .show( ) so it can function similuarly as NeoPixel calls **Author:** Chris Bruce, [email protected], 12/3/2018 """ super(ledStrip, self).show() # extended functionality self.app.processEvents() def closeEvent(self, event): print("Closing") self._on_btn_stop_pressed() self.destroy() self.app.exit()
class WrapperWidget(QWidget): def __init__(self, plugin): super().__init__() self.plugin = plugin self.setLayout(QHBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.layout().addStretch() self.inner_layout = QVBoxLayout() self.inner_layout.setContentsMargins(0, 0, 0, 0) self.label_layout = QHBoxLayout() self.temperature_min = QLabel("", parent=self) self.temperature_max = QLabel("", parent=self) self.temperature_max.setAlignment(Qt.AlignRight | Qt.AlignVCenter) self.thermal_image = ThermalImage(80, 60, self.plugin, self) self.inner_layout.addStretch() self.inner_layout.addWidget(self.thermal_image) self.label_layout.addWidget(self.temperature_min) self.label_layout.addWidget(self.temperature_max) self.inner_layout.addLayout(self.label_layout) self.thermal_image_bar = ThermalImageBar(80, 20, self.thermal_image, self.plugin, self) self.inner_layout.addWidget(self.thermal_image_bar) self.inner_layout.addStretch() self.layout().addLayout(self.inner_layout) self.layout().addStretch() self.setWindowTitle( 'Thermal Image - Thermal Imaging Bricklet - Brick Viewer ' + BRICKV_VERSION) self.height_offset = 20 + self.label_layout.sizeHint().height( ) + 2 * self.inner_layout.spacing() self.setMinimumSize( 400, 300 + self.height_offset) # To keep pixel size at least 5 def resizeEvent(self, event): super().resizeEvent(event) pixel_size = min(self.width() // 80, (self.height() - self.height_offset) // 60) if pixel_size % 2 == 0: pixel_size -= 1 pixel_size = max(5, pixel_size) self.label_layout.sizeHint().height() self.thermal_image.scale_factor_changed(pixel_size) def closeEvent(self, _event): self.plugin.thermal_image_wrapper = None self.plugin.button_detach_image.setEnabled(True) def minimumSizeHint(self): return QSize(500, 500) def sizeHint(self): return QSize(500, 500)
class GuiWindow(QMainWindow): def __init__(self, controller, base_img_fp: str): super(GuiWindow, self).__init__() # self.app = QApplication([]) self.img_manager = ImageManager(base_img_fp) self._file_path = base_img_fp self._anim_thread = None self._anim_worker = None self.animation_widget = None self._current_chunk_size = 0 self.setWindowTitle("Don't Feel Good Inc.") self.controller = controller self.create_content() self.show() def create_content(self): self.snap_sound = PyQt5.QtMultimedia.QSound("snap_ex.wav") # Build the components first # Build a textbox for the filepath. self.outer_widget = PyQt5.QtWidgets.QWidget( self) # Dummy outer widget needed so that we can add others to it # This is the frame to hold the options. self.main_frame = PyQt5.QtWidgets.QGroupBox( self.outer_widget ) # Specifying parent=self locks it within the current window # self.main_frame.setTitle("Parameters") # The main layout for the param box and the image self.main_layout_wide = QHBoxLayout(self.main_frame) self.input_frame = QWidget(self.outer_widget) self.main_layout_wide.addWidget(self.input_frame) self.input_frame_layout = QVBoxLayout(self.input_frame) # Now, adding the parts back in... self.textbox = QLabel(self.input_frame) self.textbox.setText(self._file_path) self.load_file_button = PyQt5.QtWidgets.QPushButton(self.input_frame) self.load_file_button.setText("Browse for file") self.load_file_button.clicked.connect(self.on_load) self.gain_slider_label = PyQt5.QtWidgets.QLabel(self.input_frame) self.gain_slider_label.setText("Custom adjustment") self.gain_slider = PyQt5.QtWidgets.QSlider(Qt.Horizontal, self.input_frame) self.gain_slider.valueChanged.connect(self.on_gain_slider_adjust) # size slider self.chunk_slider_label = PyQt5.QtWidgets.QLabel(self.input_frame) self.chunk_slider_label.setText("Chunk size") self.chunk_size_slider = PyQt5.QtWidgets.QSlider( Qt.Horizontal, self.input_frame) # Start this in the middle so we don't get a div/0 self.chunk_size_slider.setSliderPosition(40) self.chunk_size_slider.valueChanged.connect( self.on_chunk_slider_adjust) # This needs to be controlled with a single button press as it'll involve reloading the object self.chunk_size_button = PyQt5.QtWidgets.QPushButton( "Apply chunk size", self.input_frame) self.chunk_size_button.clicked.connect(self.on_chunk_click) # self.go_button = PyQt5.QtWidgets.QPushButton("*snap*", self.input_frame) # # self.go_button.clicked.connect(self.on_snap) self.full_snap_button = PyQt5.QtWidgets.QPushButton( "*snap*", self.input_frame) self.full_snap_button.clicked.connect(self.display_animation) self.reset_button = PyQt5.QtWidgets.QPushButton( "Use the time stone (reset)", self.input_frame) self.reset_button.clicked.connect(self.on_reset) self.input_frame_layout.addWidget(self.textbox) self.input_frame_layout.addWidget(self.load_file_button) self.input_frame_layout.addWidget(self.chunk_slider_label) self.input_frame_layout.addWidget(self.chunk_size_slider) self.input_frame_layout.addWidget(self.chunk_size_button) self.input_frame_layout.addWidget(self.gain_slider_label) self.input_frame_layout.addWidget(self.gain_slider) # self.input_frame_layout.addWidget(self.go_button) self.input_frame_layout.addWidget(self.full_snap_button) self.input_frame_layout.addWidget(self.reset_button) # Trying something else out self.image_frame = QFrame(self) # Creating the second container self.image_widget = ImageWidget(self._file_path, self.outer_widget) self.main_layout_wide.addWidget(self.image_widget) # self.main_layout.addLayout(self.main_frame) self.setCentralWidget(self.outer_widget) self.setFixedSize(self.main_layout_wide.sizeHint()) self.on_chunk_click() # Make sure we're ready to handle an animation if the need arises # AnimationReadyEmitter.trigger.connect(self.display_animation) # self.main_frame.show() def on_load(self): dlg = QFileDialog() dlg.setFileMode(QFileDialog.ExistingFile) new_path, _ = dlg.getOpenFileName(None, "Open file", "%userprofile%\\Pictures\\") if new_path == ("", ""): return if not os.path.exists(new_path): self.textbox.setStyleSheet("color: rgb(255, 0, 0);") else: self.textbox.setStyleSheet("color: rgb(0, 0, 0);") self._file_path = new_path self.textbox.setText(self._file_path) self.img_manager = ImageManager(new_path) q_pixmap = PyQt5.QtGui.QPixmap(self._file_path) self.image_widget.setPixmap(q_pixmap) self.image_widget.resize(q_pixmap.width(), q_pixmap.height()) self.main_frame.resize(self.main_layout_wide.sizeHint()) self.setFixedSize(self.main_layout_wide.sizeHint()) def display_animation(self): self.snap_sound.play() self._anim_worker = AnimWorker(self.img_manager, True) self._anim_thread = QThread() self._anim_worker.moveToThread(self._anim_thread) self._anim_worker.anim_done.connect(self.on_animation_complete) self._anim_thread.started.connect(self._anim_worker.work) self.full_snap_button.setDisabled(True) self._anim_thread.start() @pyqtSlot(str) def on_animation_complete(self, file_path: str): """Replace the original image widget until the movie completes, and then change it back""" self.animation_widget = AnimationWidget(file_path, self.outer_widget) self.full_snap_button.setDisabled(False) # self.main_layout_wide.replaceWidget(self.image_widget, self.animation_widget) prev_image_ix = self.main_layout_wide.indexOf(self.image_widget) # self.main_layout_wide.removeWidget(self.image_widget) self.image_widget.hide() # FIXME Yuck self.main_layout_wide.addWidget(self.animation_widget, Qt.Horizontal) self.animation_widget.movie.finished.connect( self.replace_original_img_widget) # self.animation_widget.movie.setScaledSize(QSize(900, 900)) self.animation_widget.movie.start() self.animation_widget.show() @pyqtSlot() def replace_original_img_widget(self): self.animation_widget.movie.stop() # This is needed to self.animation_widget.hide() self.image_widget.show() self.main_layout_wide.removeWidget(self.animation_widget) self.main_layout_wide.insertWidget(1, self.image_widget) self.main_layout_wide.replaceWidget(self.animation_widget, self.image_widget) def on_snap(self): self.snap_sound.play() self.reload_image() def on_full_snap(self): for i in reversed(range(1, 101)): self.gain_slider.setSliderPosition(i) self.reload_image() sleep(3 / i**2) # sleep(1) def reload_image(self): """ Reload the image within the current frame, asking the underlying functions to recalculate based on its current values. """ img = self.img_manager.update_image() q_image = PyQt5.QtGui.QImage.fromData(img.read()) q_pixmap = PyQt5.QtGui.QPixmap.fromImage(q_image) self.image_widget.setPixmap(q_pixmap) def on_gain_slider_adjust(self): new_value = self.gain_slider.value() # TODO Scale this print(new_value) self.img_manager.gain = new_value if self.img_manager.chunk_size > 10: self.reload_image() def on_chunk_slider_adjust(self): if self.chunk_size_slider.value() != self._current_chunk_size: self.chunk_size_button.setDisabled(False) def on_chunk_click(self): new_value = self.chunk_size_slider.value( ) + 1 # Make sure it's never equal to 0 self.img_manager.chunk_size = new_value self.chunk_size_button.setDisabled(True) def on_reset(self): self.img_manager.reset() self.gain_slider.setValue(0) q_pixmap = PyQt5.QtGui.QPixmap(self._file_path) self.image_widget.setPixmap(q_pixmap)
class SendFundsDestination(QtWidgets.QWidget): resized_signal = QtCore.pyqtSignal() def __init__(self, parent, parent_dialog, app_config, hw_session: HwSessionInfo): QtWidgets.QWidget.__init__(self, parent) self.app_config = app_config self.parent_dialog = parent_dialog self.hw_session = hw_session self.recipients: List[SendFundsDestinationItem] = [] self.change_addresses: List[Tuple[str, str]] = [ ] # List[Tuple[address, bip32 path]] self.change_controls_visible = True self.address_widget_width = None self.inputs_total_amount = 0.0 self.fee_amount = 0.0 self.inputs_count = 0 self.values_unit = OUTPUT_VALUE_UNIT_AMOUNT self.tm_calculate_change_value = QTimer(self) self.tm_calculate_change_value.timeout.connect( self.on_tm_calculate_change_value) self.current_file_name = '' self.current_file_encrypted = False self.recent_data_files = [] # recent used data files self.setupUi(self) def setupUi(self, Form): self.setSizePolicy( QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding, QtWidgets.QSizePolicy.MinimumExpanding)) self.lay_main = QtWidgets.QVBoxLayout(Form) self.lay_main.setContentsMargins(6, 6, 6, 6) self.lay_main.setSpacing(3) # 'totals' area: self.lbl_totals = QLabel(Form) self.lbl_totals.setTextInteractionFlags( QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse) self.lay_main.addWidget(self.lbl_totals) # output definition data file labels: self.lay_data_file = QHBoxLayout() self.lay_data_file.setContentsMargins(0, 0, 0, 6) self.lay_main.addItem(self.lay_data_file) self.lbl_data_file_name = QLabel(Form) self.lay_data_file.addWidget(self.lbl_data_file_name) self.lbl_data_file_badge = QLabel(Form) self.lay_data_file.addWidget(self.lbl_data_file_badge) self.lbl_data_file_name.setTextInteractionFlags( QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse) self.lbl_data_file_badge.setTextInteractionFlags( QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse) self.lay_data_file.addStretch() # actions/options area: self.lay_actions = QHBoxLayout() self.lay_actions.setSpacing(6) self.lay_actions.setContentsMargins(0, 0, 0, 0) self.lay_main.addItem(self.lay_actions) self.btn_add_recipient = QPushButton(Form) self.btn_add_recipient.clicked.connect( partial(self.add_dest_address, 1)) self.btn_add_recipient.setAutoDefault(False) self.btn_add_recipient.setText("Add recipient") self.lay_actions.addWidget(self.btn_add_recipient) # self.btn_actions = QPushButton(Form) self.btn_actions.clicked.connect(partial(self.add_dest_address, 1)) self.btn_actions.setAutoDefault(False) self.btn_actions.setText("Actions") self.lay_actions.addWidget(self.btn_actions) # context menu for the 'Actions' button self.mnu_actions = QMenu() self.btn_actions.setMenu(self.mnu_actions) a = self.mnu_actions.addAction("Load from file...") a.triggered.connect(self.on_read_from_file_clicked) self.mnu_recent_files = self.mnu_actions.addMenu('Recent files') self.mnu_recent_files.setVisible(False) a = self.mnu_actions.addAction("Save to encrypted file...") a.triggered.connect(partial(self.save_to_file, True)) a = self.mnu_actions.addAction("Save to plain CSV file...") a.triggered.connect(partial(self.save_to_file, False)) a = self.mnu_actions.addAction("Clear recipients") a.triggered.connect(self.clear_outputs) self.lbl_output_unit = QLabel(Form) self.lbl_output_unit.setText('Values as') self.lay_actions.addWidget(self.lbl_output_unit) self.cbo_output_unit = QComboBox(Form) self.cbo_output_unit.addItems(['amount', 'percentage']) self.cbo_output_unit.setCurrentIndex(0) self.cbo_output_unit.currentIndexChanged.connect( self.on_cbo_output_unit_change) self.lay_actions.addWidget(self.cbo_output_unit) self.lay_actions.addStretch(0) # scroll area for send to (destination) addresses self.scroll_area = QtWidgets.QScrollArea() self.scroll_area.setWidgetResizable(True) self.scroll_area.setMinimumHeight(30) self.scroll_area.setSizePolicy( QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)) self.scroll_area.setFrameShape(QtWidgets.QFrame.NoFrame) self.lay_main.addWidget(self.scroll_area) self.scroll_area_widget = QtWidgets.QWidget() self.scroll_area_widget.setSizePolicy( QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred, QtWidgets.QSizePolicy.MinimumExpanding)) self.lay_scroll_area = QtWidgets.QVBoxLayout() self.lay_scroll_area.setContentsMargins(0, 0, 0, 0) self.lay_scroll_area.setSpacing(0) self.scroll_area_widget.setLayout(self.lay_scroll_area) self.scroll_area.setWidget(self.scroll_area_widget) # grid layout for destination addresses and their corresponding controls: self.lay_addresses = QtWidgets.QGridLayout() self.lay_addresses.setSpacing(3) self.lay_addresses.setContentsMargins(0, 0, 0, 0) self.lay_scroll_area.addLayout(self.lay_addresses) self.lay_scroll_area.addStretch(0) # controls for the 'change' address/amount (it's placed in the last row of the addresses grid layout): self.lbl_change_address = QLabel(self.scroll_area_widget) self.lbl_change_address.setText('Change address') self.lbl_change_address.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) self.lay_addresses.addWidget(self.lbl_change_address, 0, 0) # the 'change' address combobox: self.cbo_change_address = QtWidgets.QComboBox(self.scroll_area_widget) width = self.cbo_change_address.fontMetrics().width( 'XvqNXF23dRBksxjW3VQGrBtJw7vkhWhenQ') self.address_widget_width = width + 40 # combobox width on macos needs to be tweaked: self.cbo_change_address.setFixedWidth( self.address_widget_width + {'darwin': 5}.get(sys.platform, 0)) self.lay_addresses.addWidget(self.cbo_change_address, 0, 1) self.lbl_change_amount = QLabel(self.scroll_area_widget) self.set_change_value_label() self.lay_addresses.addWidget(self.lbl_change_amount, 0, 2) # read only editbox for the amount of the change: self.edt_change_amount = QLineEdit(self.scroll_area_widget) self.edt_change_amount.setFixedWidth(100) self.edt_change_amount.setReadOnly(True) self.edt_change_amount.setStyleSheet('background-color:lightgray') self.lay_addresses.addWidget(self.edt_change_amount, 0, 3) # label dedicated to the second-unit value (e.g percentage if the main unit is set to (Dash) amount value) self.lbl_second_unit = QLabel(self.scroll_area_widget) self.lay_addresses.addWidget(self.lbl_second_unit, 0, 4) # spacer spacer = QtWidgets.QSpacerItem(20, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum) self.lay_addresses.addItem(spacer, 0, 5) # the last row of the grid layout is dedicated to 'fee' controls self.lbl_fee = QLabel(self.scroll_area_widget) self.lbl_fee.setText('Fee [Dash]') self.lbl_fee.setAlignment(QtCore.Qt.AlignRight | QtCore.Qt.AlignTrailing | QtCore.Qt.AlignVCenter) self.lay_addresses.addWidget(self.lbl_fee, 1, 0) # the fee value editbox with the 'use default' button: self.lay_fee_value = QHBoxLayout() self.lay_fee_value.setContentsMargins(0, 0, 0, 0) self.lay_fee_value.setSpacing(0) self.lay_addresses.addItem(self.lay_fee_value, 1, 1) self.edt_fee_value = QLineEdit(self.scroll_area_widget) self.edt_fee_value.setFixedWidth(100) self.edt_fee_value.textChanged.connect(self.on_edt_fee_value_changed) self.lay_fee_value.addWidget(self.edt_fee_value) self.btn_get_default_fee = QToolButton(self.scroll_area_widget) self.btn_get_default_fee.setText('\u2b06') self.btn_get_default_fee.setFixedSize( 14, self.edt_fee_value.sizeHint().height()) self.btn_get_default_fee.setToolTip('Use default fee') self.btn_get_default_fee.clicked.connect( self.on_btn_get_default_fee_clicked) self.lay_fee_value.addWidget(self.btn_get_default_fee) self.lay_fee_value.addStretch(0) # below the addresses grid place a label dedicated do display messages self.lbl_message = QLabel(Form) self.lbl_message.setTextInteractionFlags( QtCore.Qt.LinksAccessibleByMouse | QtCore.Qt.TextSelectableByMouse) self.lbl_message.setVisible(False) self.lay_main.addWidget(self.lbl_message) # add one 'send to' address row (in most cases it will bu sufficient) self.add_dest_address(1) # load last used file names from cache mru = app_cache.get_value(CACHE_ITEM_DATA_FILE_MRU_LIST, default_value=[], type=list) if isinstance(mru, list): for file_name in mru: if os.path.exists(file_name): self.recent_data_files.append(file_name) self.update_mru_menu_items() self.retranslateUi(Form) def retranslateUi(self, Form): pass def sizeHint(self): sh = self.lay_scroll_area.sizeHint() marg_sl = self.lay_scroll_area.getContentsMargins() marg_ml = self.lay_main.getContentsMargins() if self.lbl_message.isVisible(): msg_height = self.lbl_message.height() else: msg_height = 0 sh.setHeight(sh.height() + marg_sl[1] + marg_sl[3] + self.lay_actions.sizeHint().height() + self.lbl_totals.sizeHint().height() + self.lay_data_file.sizeHint().height() + ((self.lay_main.count() - 1) * self.lay_main.spacing()) + marg_ml[1] + marg_ml[3] + msg_height) return sh def display_message(self, message, color: Optional[str] = None): if message: self.lbl_message.setText(message) if color: self.lbl_message.setStyleSheet(f'QLabel{{color:{color}}}') changed_visibility = self.lbl_message.isVisible() != True self.lbl_message.setVisible(True) else: changed_visibility = self.lbl_message.isVisible() != False self.lbl_message.setVisible(False) if changed_visibility: QtWidgets.qApp.processEvents(QEventLoop.ExcludeUserInputEvents) self.resized_signal.emit() def move_grid_layout_row(self, from_row, to_row): for col_idx in range(self.lay_addresses.columnCount()): item = self.lay_addresses.itemAtPosition(from_row, col_idx) if item: if isinstance(item, QWidgetItem): w = item.widget() self.lay_addresses.removeWidget(w) self.lay_addresses.addWidget(w, to_row, col_idx) elif isinstance(item, QLayout): self.lay_addresses.removeItem(item) self.lay_addresses.addItem(item, to_row, col_idx) elif isinstance(item, QSpacerItem): self.lay_addresses.removeItem(item) self.lay_addresses.addItem(item, to_row, col_idx) else: raise Exception('Invalid item type') def add_dest_address(self, address_count: int = 1): # make a free space in the grid-layout for new addresses, just behind the last item related to the dest address for row_idx in reversed( range(len(self.recipients), self.lay_addresses.rowCount())): self.move_grid_layout_row(row_idx, row_idx + address_count) for nr in range(address_count): rcp_item = SendFundsDestinationItem(self.scroll_area_widget, self.app_config, self.lay_addresses, len(self.recipients), self.address_widget_width) rcp_item.sig_remove_address.connect(self.remove_dest_address) rcp_item.sig_use_all_funds.connect(self.use_all_funds_for_address) rcp_item.sig_amount_changed.connect(self.amount_changed) rcp_item.set_output_value_unit(self.values_unit) rcp_item.set_inputs_total_amount(self.inputs_total_amount - self.fee_amount) self.recipients.append(rcp_item) QtWidgets.qApp.processEvents(QEventLoop.ExcludeUserInputEvents) self.resized_signal.emit() self.show_hide_remove_buttons() self.display_totals() self.set_default_fee() def remove_item_from_layout(self, item): if item: if isinstance(item, QWidgetItem): w = item.widget() self.lay_addresses.removeWidget(w) w.setParent(None) del w elif isinstance(item, QLayout): for subitem_idx in reversed(range(item.count())): subitem = item.itemAt(subitem_idx) self.remove_item_from_layout(subitem) self.lay_addresses.removeItem(item) item.setParent(None) del item elif isinstance(item, QSpacerItem): del item else: raise Exception('Invalid item type') def remove_dest_address(self, address_item): row_idx = self.recipients.index(address_item) # remove all widgets related to the 'send to' address that is being removed for col_idx in range(self.lay_addresses.columnCount()): item = self.lay_addresses.itemAtPosition(row_idx, col_idx) self.remove_item_from_layout(item) # move up all rows greater than the row being removed for row in range(row_idx + 1, len(self.recipients)): self.move_grid_layout_row(row, row - 1) del self.recipients[row_idx] QtWidgets.qApp.processEvents(QEventLoop.ExcludeUserInputEvents) self.resized_signal.emit() self.show_hide_remove_buttons() self.set_default_fee() # self.calculate_change_amount() self.display_totals() def use_all_funds_for_address(self, address_item): row_idx = self.recipients.index(address_item) sum = 0.0 left = 0.0 # sum all the funds in all rows other than the current one for idx, addr in enumerate(self.recipients): if idx != row_idx: sum += addr.get_value(default_value=0.0) if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT: left = self.inputs_total_amount - sum - self.fee_amount elif self.values_unit == OUTPUT_VALUE_UNIT_PERCENT: left = 100.0 - sum left = round(left, 8) + 0.0 if left < 0: left = 0.0 address_item.set_value(left) self.update_change_amount() def amount_changed(self, addres_item): """ Activated after changing value in the 'amount' edit box of a recipient address. """ self.init_calculate_change_value() def on_edt_fee_value_changed(self, text): if not text: text = '0.0' try: self.fee_amount = float(text) self.init_calculate_change_value() except Exception: self.display_message('Invalid \'transaction fee\' value.', 'red') # display error message def show_hide_change_address(self, visible): if visible != self.change_controls_visible: row_nr = self.lay_addresses.rowCount() - 1 if row_nr >= 0: for col_idx in range(self.lay_addresses.columnCount()): item = self.lay_addresses.itemAtPosition(row_nr, col_idx) if item: if isinstance(item, QWidgetItem): item.widget().setVisible(visible) elif isinstance( item, (QSpacerItem, QHBoxLayout, QVBoxLayout)): pass else: raise Exception('Invalid item type') self.change_controls_visible = visible QtWidgets.qApp.processEvents(QEventLoop.ExcludeUserInputEvents) self.resized_signal.emit() def show_hide_remove_buttons(self): visible = len(self.recipients) > 1 for item in self.recipients: item.set_btn_remove_address_visible(visible) def set_change_addresses(self, addresses: List[Tuple[str, str]]): """ :param addresses: addresses[0]: dest change address addresses[1]: dest change bip32 :return: """ self.cbo_change_address.clear() self.change_addresses.clear() for addr in addresses: self.cbo_change_address.addItem(addr[0]) self.change_addresses.append((addr[0], addr[1])) def set_input_amount(self, amount, inputs_count): self.inputs_count = inputs_count if amount != self.inputs_total_amount or inputs_count != self.inputs_count: # if there is only one recipient address and his current amount equals to the # previuus input_amount, assign new value to him last_total_amount = self.inputs_total_amount last_fee_amount = self.fee_amount self.inputs_total_amount = amount self.fee_amount = self.calculate_fee() if (len(self.recipients) == 1 or self.recipients[0].get_value(default_value=0.0) == 0.0 or self.recipients[0].get_value(default_value=0.0) == round(last_total_amount - last_fee_amount, 8)): if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT: amount_minus_fee = round(amount - self.fee_amount, 8) if amount_minus_fee < 0: amount_minus_fee = 0.0 self.recipients[0].set_value(amount_minus_fee) elif self.values_unit == OUTPUT_VALUE_UNIT_PERCENT: self.recipients[0].set_value(100.0) old_state = self.edt_fee_value.blockSignals(True) self.edt_fee_value.setText(app_utils.to_string(self.fee_amount)) self.edt_fee_value.blockSignals(old_state) for addr in self.recipients: addr.set_inputs_total_amount(amount - self.fee_amount) addr.clear_validation_results() self.edt_fee_value.update() self.update_change_amount() self.display_totals() def calculate_change_value(self) -> float: """Returns the change value in Dash.""" sum = 0.0 for addr in self.recipients: sum += addr.get_value(default_value=0.0) if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT: change_amount = round( self.inputs_total_amount - sum - self.fee_amount, 8) + 0 # eliminate -0.0 elif self.values_unit == OUTPUT_VALUE_UNIT_PERCENT: change_amount = round( (100.0 - sum) * (self.inputs_total_amount - self.fee_amount) / 100, 8) else: raise Exception('Invalid unit') return change_amount def update_change_amount(self) -> None: change_amount = self.calculate_change_value() if self.inputs_total_amount - self.fee_amount != 0: change_pct = round( change_amount * 100 / (self.inputs_total_amount - self.fee_amount), 8) + 0 else: change_pct = 0.0 if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT: left_second_unit_str = app_utils.to_string(round(change_pct, 3)) + '%' self.edt_change_amount.setText( app_utils.to_string(round(change_amount, 8))) elif self.values_unit == OUTPUT_VALUE_UNIT_PERCENT: left_second_unit_str = app_utils.to_string(round(change_amount, 8)) + ' Dash' sum = 0.0 for addr in self.recipients: sum += addr.get_value(default_value=0.0) self.edt_change_amount.setText( app_utils.to_string(round(100 - sum, 8))) else: raise Exception('Invalid unit') msg = '' if change_amount < 0: used_amount = round(self.inputs_total_amount - change_amount, 8) + 0 msg = f'Not enough funds - used amount: ' \ f'{used_amount}, available: {self.inputs_total_amount}. Adjust ' \ f'the output values.' self.lbl_second_unit.setText(left_second_unit_str) self.display_message(msg, 'red') def validate_output_data(self) -> bool: ret = True for addr in self.recipients: if not addr.validate(): ret = False if not ret: self.display_message( 'Data of at least one recipient is invalid or empty. ' 'Please correct the data to continue.', 'red') else: self.display_message('') return ret def on_tm_calculate_change_value(self): self.tm_calculate_change_value.stop() for addr_item in self.recipients: addr_item.set_inputs_total_amount(self.inputs_total_amount - self.fee_amount) self.update_change_amount() def calculate_fee(self): if self.inputs_total_amount > 0.0: bytes = (self.inputs_count * 148) + (len(self.recipients) * 34) + 10 fee = round(bytes * FEE_SAT_PER_BYTE, 8) if not fee: fee = MIN_TX_FEE fee = round(fee / 1e8, 8) else: fee = 0.0 return fee def get_tx_fee(self): if self.fee_amount < 0.0: raise Exception('Invalid the fee value.') return round(self.fee_amount * 1e8) def init_calculate_change_value(self): self.tm_calculate_change_value.start(100) def set_default_fee(self): self.fee_amount = self.calculate_fee() old_status = self.edt_fee_value.blockSignals(True) try: self.edt_fee_value.setText(app_utils.to_string(self.fee_amount)) finally: self.edt_fee_value.blockSignals(old_status) self.edt_fee_value.update() self.init_calculate_change_value() def on_btn_get_default_fee_clicked(self): self.set_default_fee() def set_dest_addresses(self, addresses: List): if len(addresses) > 0: count_diff = len(addresses) - len(self.recipients) if count_diff > 0: self.add_dest_address(count_diff) elif count_diff < 0: # remove unecessary rows, beginning from the largest one for nr in reversed(range(len(addresses), len(self.recipients))): self.remove_dest_address(self.recipients[nr]) for idx, addr_item in enumerate(self.recipients): if isinstance(addresses[idx], (list, tuple)): # passed address-value tuple if len(addresses[idx]) >= 1: addr_item.set_address(addresses[idx][0]) if len(addresses[idx]) >= 2: addr_item.set_value(addresses[idx][1]) else: addr_item.set_address(addresses[idx]) self.display_totals() def set_change_value_label(self): if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT: self.lbl_change_amount.setText('value') self.lbl_change_amount.setToolTip( 'Unused amount - will be sent back to the change address') else: self.lbl_change_amount.setText('pct. value') self.lbl_change_amount.setToolTip( 'Unused amount (as percent of the total value of all inputs) - will ' 'be sent back to the change address') def on_cbo_output_unit_change(self, index): if index == 0: self.values_unit = OUTPUT_VALUE_UNIT_AMOUNT else: self.values_unit = OUTPUT_VALUE_UNIT_PERCENT self.set_change_value_label() for addr_item in self.recipients: addr_item.set_output_value_unit(self.values_unit) self.update_change_amount() def update_ui_value_unit(self): if self.values_unit == OUTPUT_VALUE_UNIT_AMOUNT: self.cbo_output_unit.setCurrentIndex(0) else: self.cbo_output_unit.setCurrentIndex(1) def simplyfy_file_home_dir(self, file_name): home_dir = os.path.expanduser('~') if self.current_file_name.find(home_dir) == 0: file_name = '~' + self.current_file_name[len(home_dir):] else: file_name = self.current_file_name return file_name def display_totals(self): bytes = (self.inputs_count * 148) + (len(self.recipients) * 34) + 10 text = f'<span class="label"><b>Total value of selected inputs:</b> </span><span class="value"> {self.inputs_total_amount} Dash </span>' if self.inputs_total_amount > 0: text += f'<span class="label"> <b>Inputs:</b> </span><span class="value"> {self.inputs_count} </span>' \ f'<span class="label"> <b>Outputs:</b> </span><span class="value"> {len(self.recipients)} </span>' \ f'<span class="label"> <b>Transaction size:</b> </span><span class="value"> {bytes} B </span>' self.lbl_totals.setText(text) if self.current_file_name: file_name = self.simplyfy_file_home_dir(self.current_file_name) text = f'<span class="label"><b>File:</b> </span><span class="value">{file_name} </span>' self.lbl_data_file_name.setText(text) self.lbl_data_file_name.setVisible(True) self.lbl_data_file_badge.setVisible(True) if self.current_file_encrypted: self.lbl_data_file_badge.setText('Encrypted') self.lbl_data_file_badge.setStyleSheet( "QLabel{background-color:#2eb82e;color:white; padding: 1px 3px 1px 3px; border-radius: 3px;}" ) else: self.lbl_data_file_badge.setText('Not encrypted') self.lbl_data_file_badge.setStyleSheet( "QLabel{background-color:orange;color:white; padding: 1px 3px 1px 3px; border-radius: 3px;}" ) else: self.lbl_data_file_name.setVisible(False) self.lbl_data_file_badge.setVisible(False) def clear_outputs(self): if WndUtils.queryDlg("Do you really want to clear all outputs?", default_button=QMessageBox.Cancel, icon=QMessageBox.Warning) == QMessageBox.Ok: self.set_dest_addresses([('', '')]) self.use_all_funds_for_address(self.recipients[0]) def save_to_file(self, save_encrypted): if self.current_file_name and os.path.exists( os.path.dirname(self.current_file_name)): dir = os.path.dirname(self.current_file_name) else: dir = self.app_config.data_dir if save_encrypted: initial_filter = "DAT files (*.dat)" else: initial_filter = "CSV files (*.csv)" file_filter = f"{initial_filter};;All Files (*)" file_name = WndUtils.save_file_query( self.parent_dialog, message='Enter the file name to save the data.', directory=dir, filter=file_filter, initial_filter=initial_filter) if file_name: data = bytes() data += b'RECIPIENT_ADDRESS\tVALUE\n' if self.values_unit == OUTPUT_VALUE_UNIT_PERCENT: suffix = '%' else: suffix = '' for addr in self.recipients: line = f'{addr.get_address()}{CSV_SEPARATOR}{str(addr.get_value(default_value=""))}{suffix}\n' data += line.encode('utf-8') if save_encrypted: write_file_encrypted(file_name, self.hw_session, data) else: with open(file_name, 'wb') as f_ptr: f_ptr.write(data) self.current_file_name = file_name self.current_file_encrypted = save_encrypted self.add_menu_item_to_mru(self.current_file_name) self.update_mru_menu_items() self.display_totals() def on_read_from_file_clicked(self): try: if self.current_file_name and os.path.exists( os.path.dirname(self.current_file_name)): dir = os.path.dirname(self.current_file_name) else: dir = self.app_config.data_dir initial_filter1 = "DAT files (*.dat)" initial_filter2 = "CSV files (*.csv)" file_filter = f"{initial_filter1};;{initial_filter2};;All Files (*.*)" file_name = WndUtils.open_file_query( self.parent_dialog, message='Enter the file name to read the data.', directory=dir, filter=file_filter, initial_filter='All Files (*.*)') if file_name: self.read_from_file(file_name) except Exception as e: self.parent_dialog.errorMsg(str(e)) def read_from_file(self, file_name): try: file_info = {} data_decrypted = bytearray() for block in read_file_encrypted(file_name, file_info, self.hw_session): data_decrypted.extend(block) file_encrypted = file_info.get('encrypted', False) data = data_decrypted.decode('utf-8') addresses = [] value_unit = None for line_idx, line in enumerate(data.split('\n')): if line: elems = line.split('\t') if len(elems) < 2: elems = line.split(';') if len(elems) < 2: raise ValueError( f'Invalid data file entry for line: {line_idx+1}.') address = elems[0].strip() value = elems[1].strip() address_valid = dash_utils.validate_address( address, dash_network=None) if not address_valid: if line_idx == 0 and re.match(r'^[A-Za-z_]+$', address): continue # header line else: raise ValueError( f'Invalid recipient address ({address}) (line {line_idx+1}).' ) if value.endswith('%'): vu = OUTPUT_VALUE_UNIT_PERCENT value = value[:-1] else: vu = OUTPUT_VALUE_UNIT_AMOUNT if value_unit is None: value_unit = vu elif value_unit != vu: raise ValueError( f'The value unit in line {line_idx+1} differs from the previous ' f'line.') try: if value: value = float(value.replace(',', '.')) else: value = None addresses.append((address, value)) except Exception as e: raise ValueError( f'Invalid data in the \'value\' field (line {line_idx+1}).' ) if len(addresses) == 0: raise Exception('File doesn\'t contain any recipient\'s data.') else: if self.values_unit != value_unit: self.values_unit = value_unit self.update_ui_value_unit() self.set_dest_addresses(addresses) self.current_file_name = file_name self.current_file_encrypted = file_encrypted self.add_menu_item_to_mru(self.current_file_name) self.update_mru_menu_items() self.update_change_amount() self.display_totals() except Exception as e: self.update_mru_menu_items() logging.exception( 'Exception while reading file with recipients data.') self.parent_dialog.errorMsg(str(e)) def add_menu_item_to_mru(self, file_name: str) -> None: if file_name: try: if file_name in self.recent_data_files: idx = self.recent_data_files.index(file_name) del self.recent_data_files[idx] self.recent_data_files.insert(0, file_name) else: self.recent_data_files.insert(0, file_name) app_cache.set_value(CACHE_ITEM_DATA_FILE_MRU_LIST, self.recent_data_files) except Exception as e: logging.warning(str(e)) def update_mru_menu_items(self): app_utils.update_mru_menu_items(self.recent_data_files, self.mnu_recent_files, self.on_data_file_mru_action_triggered, self.current_file_name, self.on_act_clear_mru_items) def on_act_clear_mru_items(self): self.recent_data_files.clear() app_cache.set_value(CACHE_ITEM_DATA_FILE_MRU_LIST, self.recent_data_files) self.update_mru_menu_items() def on_data_file_mru_action_triggered(self, file_name: str) -> None: """ Triggered by clicking one of the subitems of the 'Open Recent' menu item. Each subitem is related to one of recently openend data files. :param file_name: A data file name accociated with the menu action clicked. """ self.read_from_file(file_name) def get_tx_destination_data(self) -> List[Tuple[str, int, str]]: """ :return: Tuple structure: [0]: dest address [1]: value in satoshis/duffs [2]: bip32 path of the address if the item is a change address, otherwise None """ if self.validate_output_data(): change_amount = self.calculate_change_value() if change_amount < 0.0: self.update_change_amount( ) # here an appropriate message will be displayed raise Exception('Not enough funds!!!') dest_data = [] for addr in self.recipients: dest_addr = addr.get_address() value = round(addr.get_value_amount() * 1e8) dest_data.append((dest_addr, value, None)) if change_amount > 0.0: change_address_idx = self.cbo_change_address.currentIndex() if change_address_idx >= 0 and change_address_idx < len( self.change_addresses): dest_data.append( (self.change_addresses[change_address_idx][0], round(change_amount * 1e8), self.change_addresses[change_address_idx][1])) else: raise Exception('Invalid address for the change.') return dest_data else: return [] def get_recipients_list(self) -> List[Tuple[str, ]]: """ :return: List of recipient addresses List[Tuple[str <address>, float <value>] """ dest_data = [] for addr in self.recipients: dest_addr = addr.get_address() if dest_addr: dest_data.append((dest_addr, )) return dest_data
class TAAllocationOutputTab(QScrollArea): def __init__(self, ctx, a): super(TAAllocationOutputTab, self).__init__() self.ctx = ctx self.a = a self.wgtMain = QWidget() self.wgtMain.setSizePolicy(QSizePolicy.Fixed, QSizePolicy.MinimumExpanding) self.hboxMain = QHBoxLayout(self.wgtMain) self.allocation = self.ctx.app_data.results[a] self.drag_lists = [] self.labels = [] self.wgtSubs = [] self.wgtSubLyts = [] for t, table in enumerate(self.allocation): wgtSub = QGroupBox("Group {}".format(t + 1)) wgtSub.setSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding) vboxSub = QVBoxLayout(wgtSub) self.wgtSubs.append(wgtSub) self.wgtSubLyts.append(vboxSub) drag_list = TAListDragAndDrop() drag_list.setMinimumHeight(200) drag_list.trigger.connect(self.update_tables_by_user) self.drag_lists.append(drag_list) vboxSub.addWidget(drag_list) label = QLabel() label.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum) self.labels.append(label) vboxSub.addWidget(label) self.hboxMain.addWidget(wgtSub) self.setWidget(self.wgtMain) self.set_content() def set_content(self): for t, table in enumerate(self.allocation): drag_list = self.drag_lists[t] label = self.labels[t] order_cluster_dict = self.ctx.app_data_manager.get_fields_cluster_dict( ) order_diverse_dict = self.ctx.app_data_manager.get_fields_diverse_dict( ) cats = {**order_cluster_dict, **order_diverse_dict} # set drag_list items drag_list.clear() r = 0 for pid in table: newItem = QListWidgetItem() newItem.setData(Qt.UserRole, pid) print_label = self.ctx.app_data_manager.get_print_labels(pid) newItem.setText(print_label) tooltip_infos = [print_label] for field_key in cats: field_key_label = self.ctx.app_data.peopledata_keys[ field_key] field_pid_val = self.ctx.app_data_manager.load_details( pid, field_key) tooltip_infos.append("{}: {}".format( field_key_label, field_pid_val)) newItem.setToolTip("\n".join(tooltip_infos)) drag_list.insertItem(r, newItem) r += 1 label_strings = [ "<p><strong>Total: {}</strong></p>".format(len(table)) ] for cat_key, cat_val_terms in cats.items(): label_strings.append("<p>{}:<br/>".format( self.ctx.app_data.peopledata_keys[cat_key])) label_strings_cat = [] for cat_val_term in cat_val_terms: occurences = self.ctx.app_data_manager.get_occurences( self.a, t, cat_key, cat_val_term) label_strings_cat.append("{} {}".format( occurences, cat_val_term)) label_strings.append("<br/>".join(label_strings_cat)) label_strings.append("</p>") label_string = " ".join(label_strings) label.setText(label_string) self.wgtMain.resize(self.hboxMain.sizeHint().width(), self.wgtSubs[0].sizeHint().height() + 30) def update_tables_by_user(self): new_allocation = [[] for t in self.allocation] for t, table in enumerate(self.allocation): new_allocation[t] = [ self.drag_lists[t].item(i).data(Qt.UserRole) for i in range(self.drag_lists[t].count()) ] self.ctx.app_data.results[self.a] = new_allocation self.allocation = new_allocation self.ctx.set_unsaved() self.set_content()
def __init__(self): super(DisplayWindowSP, self).__init__() from douyu_client_gui import CHINESE_SIZE self.connect_widget = QWidget(self) self.room_id_list_config_button = QPushButton(u'设置监视的直播间列表', self) self.anchor_blacklist_config_button = QPushButton(u'设置主播黑名单', self) self.anchor_blacklist_check = QCheckBox(u'启用主播黑名单', self) self.room_id_list_config_widget = TextEditWidget(self) self.anchor_blacklist_config_widget = TextEditWidget(self) self.room_id_list_config_button.setCursor(POINT_HAND_CURSOR) self.anchor_blacklist_config_button.setCursor(POINT_HAND_CURSOR) self.room_id_list_config_button.setFixedWidth(CHINESE_SIZE.width() * 12) self.anchor_blacklist_config_button.setFixedWidth( CHINESE_SIZE.width() * 12) self.room_id_list_config_widget.setWindowTitle(u'设置监视的直播间列表') self.anchor_blacklist_config_widget.setWindowTitle(u'设置主播黑名单') self.topbar_widget.roomid_enter.setText('000000') self.topbar_widget.roomid_enter.setDisabled(True) self.topbar_widget.connect_danmu.setParent(self.connect_widget) self.topbar_widget.hide() self.tab_window.removeTab(0) self.tab_window.removeTab(1) connect_layout = QHBoxLayout() connect_layout.addWidget(self.anchor_blacklist_check) connect_layout.addWidget(self.anchor_blacklist_config_button) connect_layout.addStretch(1) connect_layout.addWidget(self.room_id_list_config_button) connect_layout.addWidget(self.topbar_widget.connect_danmu) connect_layout.setContentsMargins(10, 10, 10, 0) self.connect_widget.setLayout(connect_layout) self.connect_widget.resize(connect_layout.sizeHint()) self.layout().insertWidget(0, self.connect_widget) self.room_id = '000000' self.connected_room_id_list = [] self.room_id_list = [] # 连接的直播间号列表 self.anchor_blacklist_enabled = False self.anchor_blacklist = [] # 主播黑名单 self.queue_rid_order = {} self.queue_server_data = {} self.queue_message_data = {} self.queue_order_except_1 = {} self.queue_order_except_2 = {} self.dsp_temp = {} # 广播消息显示缓存 self.config_sp_file = os.path.join(self.room_config_path, 'config_sp') self.browser_list_file = os.path.join(PROGRAM_CONFIG_FOLDER, 'BrowserList') self.browser_list = self.load_browser_list() self.update_details_timer.timeout.disconnect() self.config_widget.save_config_button.clicked.connect( self.load_browser_event) self.room_id_list_config_button.clicked.connect( self.room_id_list_config_button_event) self.anchor_blacklist_config_button.clicked.connect( self.anchor_blacklist_config_button_event) self.anchor_blacklist_check.clicked.connect( self.anchor_blacklist_check_event) self.room_id_list_config_widget.save_button.clicked.connect( self.room_id_list_config_save_event) self.anchor_blacklist_config_widget.save_button.clicked.connect( self.anchor_blacklist_config_save_event) self.room_id_list_config_widget.cancel_button.clicked.connect( self.room_id_list_config_widget.hide) self.anchor_blacklist_config_widget.cancel_button.clicked.connect( self.anchor_blacklist_config_widget.hide) self.load_config_sp()