def show_tray(self): if QSystemTrayIcon.isSystemTrayAvailable: # create tray menu traymenu = QMenu('AyoShalat', self) openwin_menu = traymenu.addAction('Show me!') openwin_menu.triggered.connect(self.show) playazan_menu = traymenu.addAction('Play Azan') playazan_menu.triggered.connect(self.playAzan) stop_azan_menu = traymenu.addAction('Stop Azan') stop_azan_menu.triggered.connect(self.stopAzan) traymenu.addSeparator() exit_menu = traymenu.addAction('Exit') exit_menu.triggered.connect(self.exit) # create tray icon qtray = QSystemTrayIcon(self) qtray.setIcon(QIcon(self.icopath)) qtray.setVisible(True) qtray.setContextMenu(traymenu) qtray.show()
class AyoShalat(QMainWindow): # +=========+===============================================+ # | Method | Description | # +=========+===============================================+ # | MWL | Muslim World League | # +---------+-----------------------------------------------+ # | ISNA | Islamic Society of North America | # +---------+-----------------------------------------------+ # | Egypt | Egyptian General Authority of Survey | # +---------+-----------------------------------------------+ # | Makkah | Umm al-Qura University | # +---------+-----------------------------------------------+ # | Karachi | University of Islamic Sciences, Karachi | # +---------+-----------------------------------------------+ # | Tehran | Institute of Geophysics, University of Tehran | # +---------+-----------------------------------------------+ # | Jafari | Shia Ithna Ashari (Jafari) | # +---------+-----------------------------------------------+ def __init__(self): QMainWindow.__init__(self) self.ui = Ui_MainWindow() self.ui.setupUi(self) if os.name == 'nt': self.current_directory = str( pathlib.Path(__file__).parent.absolute()).replace('\\', '/') else: self.current_directory = str( pathlib.Path(__file__).parent.absolute()) self.reformat_ui() # set clicked self.ui.btnPlay.clicked.connect(self.playAzan) self.ui.btnStop.clicked.connect(self.stopAzan) # self.ui.btnHide.clicked.connect(self.openSetting) self.ui.btnHide.clicked.connect(self.hide) self.ui.btnExit.clicked.connect(self.do_close) self.ui.btnSave.clicked.connect(self.do_save) self.ui.btnSetting.clicked.connect(self.show_frame_setting) self.ui.btnTimeTable.clicked.connect(self.show_time_table) self.ui.ckNotification.toggled.connect(self.toggle_check_notification) self.calculation_method_array = [ 'MWL', 'ISNA', 'Egypt', 'Makkah', 'Karachi', 'Tehran', 'Jafari' ] self.mathhab_array = ['Standard', 'Hanafi'] # init form setting self.ui.frameSetting.setVisible(False) # get username of this login user # self.myusername = pwd.getpwuid(os.getuid()).pw_name # init icon self.setting_file = self.current_directory + '/setting.txt' self.icopath = self.current_directory + '/icon/masjid.xpm' self.setWindowIcon(QIcon(self.icopath)) self.default_azan = self.current_directory + '/audio/azan.mp3' self.default_azan_wav = self.current_directory + '/audio/azan.wav' self.default_notif = self.current_directory + '/audio/hayyalashala.mp3' self.default_notif_wav = self.current_directory + '/audio/hayyalashala.wav' # image dialog azandialog = QDialog(self, Qt.FramelessWindowHint) azandialog.setWindowTitle("It's time to Shalat") self.azanDialog = azandialog btnDialog = QPushButton("", self.azanDialog) btnDialog.setFlat(True) btnDialog.clicked.connect(self.azanDialog.hide) # OPEN SETTING ON START self.open_setting() # init thread self.docalc = threading.Thread(target=self.do_calculate, name="Azan Calculating") self.threadAzan = threading.Thread(target=self._playAzan, name="Play Azan") self.threadNotif = threading.Thread(target=self._playNotif, name="Play Notif") self.init_times_new() # show times self.showTimes() # show tray on start self.show_tray() # --------------------------------------------------------------------------------------------------------------------- def do_close(self): if QMessageBox().question(self, 'Close', 'Are you sure ?', QMessageBox.StandardButton.Yes, QMessageBox.StandardButton.No ) == QMessageBox.StandardButton.Yes: self.qtray.hide() self.qtray.deleteLater() self.deleteLater() os._exit(0) def toggle_check_notification(self): # toggle enable of txNotification if self.ui.ckNotification.isChecked(): self.ui.txBeforeTime.show() self.ui.label_7.show() else: self.ui.txBeforeTime.hide() self.ui.label_7.hide() def show_frame_setting(self): self.ui.frameSetting.setVisible(True) self.ui.frameSetting.raise_() def show_time_table(self): self.ui.frameSetting.setVisible(False) def show_tray(self): if QSystemTrayIcon.isSystemTrayAvailable: # create tray menu traymenu = QMenu('AyoShalat', self) openwin_menu = traymenu.addAction('Show me!') openwin_menu.triggered.connect(self.show) playazan_menu = traymenu.addAction('Play Azan') playazan_menu.triggered.connect(self.playAzan) stop_azan_menu = traymenu.addAction('Stop Azan') stop_azan_menu.triggered.connect(self.stopAzan) traymenu.addSeparator() exit_menu = traymenu.addAction('Exit') exit_menu.triggered.connect(self.do_close) # create tray icon self.qtray = QSystemTrayIcon(self) self.qtray.setIcon(QIcon(self.icopath)) self.qtray.setVisible(True) self.qtray.setContextMenu(traymenu) self.qtray.show() def do_save(self): # save setting to tinydb if self.ui.ckStartTray.isChecked(): is_open_in_tray = 'True' else: is_open_in_tray = 'False' vals = { 'open_in_tray': is_open_in_tray, 'latitude': self.ui.txLat.text().strip(), 'longitude': self.ui.txLong.text().strip(), 'utc_offset': self.ui.txUtc.text().strip(), 'calculation_method_index': str(self.ui.cbMethod.currentIndex()), 'time_format': '24h', 'mathhab_index': str(self.ui.cbMathhab.currentIndex()), 'before_pray_time': self.ui.txBeforeTime.text().strip(), 'enable_notification_before': str(self.ui.ckNotification.isChecked()), 'before_jumah_time': self.ui.txBeforeJumah.text().strip(), 'enable_jumah_notification': str(self.ui.ckJumah.isChecked()), } pprint(vals) # setting_lines = self.db.search(self.TinyData.code == 'setting') # for setting in setting_lines: # setting['latitude'] = self.ui.txLat.text().strip() # setting['longitude'] = self.ui.txLong.text().strip() # setting['utc_offset'] = self.ui.txUtc.text().strip() # setting['calculation_method_index'] = str(self.ui.cbMethod.currentIndex()) # setting['time_format'] = '24h' # setting['mathhab_index'] = str(self.ui.cbMathhab.currentIndex()) self.db.update(vals, self.TinyData.code == 'setting') # reload setting self.open_setting() self.init_times_new() self.showTimes() # active_time fajr = 1 def set_color_prayer_time_frame(self, active_time): normal_style = "border-style:solid;border-radius:0px;border-bottom:1px solid whitesmoke;" active_style = normal_style + "background-color:rgb(252, 210, 93);" normal_style = ".QFrame{" + normal_style + "}" active_style = ".QFrame{" + active_style + "}" # clear self.ui.frameFajr.setStyleSheet(normal_style) self.ui.frameDhuhr.setStyleSheet(normal_style) self.ui.frameAsr.setStyleSheet(normal_style) self.ui.frameMaghrib.setStyleSheet(normal_style) self.ui.frameIsya.setStyleSheet(normal_style) if active_time == 1: self.ui.frameFajr.setStyleSheet(active_style) if active_time == 2: self.ui.frameDhuhr.setStyleSheet(active_style) if active_time == 3: self.ui.frameAsr.setStyleSheet(active_style) if active_time == 4: self.ui.frameMaghrib.setStyleSheet(active_style) if active_time == 5: self.ui.frameIsya.setStyleSheet(active_style) def show_prayer_time_info(self): # show current prayer time info # get current time current_time = datetime.datetime.now() now = current_time.strftime("%H:%M") # if now == self.time_array['fajr'].strip(): # self.ui.lblCurrentWaktu.setText('Subh') # self.set_color_prayer_time_frame(1) # elif now == self.time_array['dhuhr'].strip(): # self.ui.lblCurrentWaktu.setText('dhuhr') # self.set_color_prayer_time_frame(2) # elif now == self.time_array['asr'].strip(): # self.ui.lblCurrentWaktu.setText('Asr') # self.set_color_prayer_time_frame(3) # elif now == self.time_array['maghrib'].strip(): # self.ui.lblCurrentWaktu.setText('Maghrib') # self.set_color_prayer_time_frame(4) # elif now == self.time_array['isha'].strip(): # self.ui.lblCurrentWaktu.setText('Isya`') # self.set_color_prayer_time_frame(5) # subh subh = self.time_array['fajr'].strip() dhuhr = self.time_array['dhuhr'].strip() asr = self.time_array['asr'].strip() maghrib = self.time_array['maghrib'].strip() isya = self.time_array['isha'].strip() subh_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d')) + ' ' + subh + ':00' subh_date = datetime.datetime.strptime(subh_date_str, '%Y/%m/%d %H:%M:%S') # dhuhr dhuhr_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d')) + ' ' + dhuhr + ':00' dhuhr_date = datetime.datetime.strptime(dhuhr_date_str, '%Y/%m/%d %H:%M:%S') # asr asr_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d')) + ' ' + asr + ':00' asr_date = datetime.datetime.strptime(asr_date_str, '%Y/%m/%d %H:%M:%S') # asr maghrib_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d')) + ' ' + maghrib + ':00' maghrib_date = datetime.datetime.strptime(maghrib_date_str, '%Y/%m/%d %H:%M:%S') # isya isya_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d')) + ' ' + isya + ':00' isya_date = datetime.datetime.strptime(isya_date_str, '%Y/%m/%d %H:%M:%S') # show upcoming if current_time >= subh_date and current_time <= dhuhr_date: self.ui.lblUpcomingWaktu.setText("Dhuhr") self.ui.lblUpcomingJam.setText(self.time_array['dhuhr']) self.ui.lblCurrentWaktu.setText('Subh') self.show_remaining_time() self.set_color_prayer_time_frame(1) if current_time >= dhuhr_date and current_time <= asr_date: self.ui.lblUpcomingWaktu.setText("Asr") self.ui.lblUpcomingJam.setText(self.time_array['asr']) self.ui.lblCurrentWaktu.setText('Duhr') self.show_remaining_time() self.set_color_prayer_time_frame(2) if current_time >= asr_date and current_time <= maghrib_date: self.ui.lblUpcomingWaktu.setText("Maghrib") self.ui.lblUpcomingJam.setText(self.time_array['maghrib']) self.ui.lblCurrentWaktu.setText('Asr') self.show_remaining_time() self.set_color_prayer_time_frame(3) if current_time >= maghrib_date and current_time <= isya_date: self.ui.lblUpcomingWaktu.setText("Isya") self.ui.lblUpcomingJam.setText(self.time_array['isha']) self.ui.lblCurrentWaktu.setText('Maghrib') self.show_remaining_time() self.set_color_prayer_time_frame(4) # if current_time >= isya_date and current_time <= subh_date: if current_time >= isya_date: self.ui.lblUpcomingWaktu.setText("Subh") self.ui.lblUpcomingJam.setText(self.time_array['fajr']) self.ui.lblCurrentWaktu.setText('Isya') self.show_remaining_time() self.set_color_prayer_time_frame(5) def show_remaining_time(self): current_time = datetime.datetime.now() subh = self.time_array['fajr'].strip() dhuhr = self.time_array['dhuhr'].strip() asr = self.time_array['asr'].strip() maghrib = self.time_array['maghrib'].strip() isya = self.time_array['isha'].strip() subh_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d')) + ' ' + subh + ':00' subh_date = datetime.datetime.strptime(subh_date_str, '%Y/%m/%d %H:%M:%S') # dhuhr dhuhr_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d')) + ' ' + dhuhr + ':00' dhuhr_date = datetime.datetime.strptime(dhuhr_date_str, '%Y/%m/%d %H:%M:%S') # asr asr_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d')) + ' ' + asr + ':00' asr_date = datetime.datetime.strptime(asr_date_str, '%Y/%m/%d %H:%M:%S') # asr maghrib_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d')) + ' ' + maghrib + ':00' maghrib_date = datetime.datetime.strptime(maghrib_date_str, '%Y/%m/%d %H:%M:%S') # isya isya_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d')) + ' ' + isya + ':00' isya_date = datetime.datetime.strptime(isya_date_str, '%Y/%m/%d %H:%M:%S') # show upcoming if current_time >= subh_date and current_time <= dhuhr_date: remaining_hours = int( self.get_remaining_second(current_time, dhuhr_date) // 3600) remaining_minutes = int( (self.get_remaining_second(current_time, dhuhr_date) // 60) % 60) if current_time >= dhuhr_date and current_time <= asr_date: remaining_hours = int( self.get_remaining_second(current_time, asr_date) // 3600) remaining_minutes = int( (self.get_remaining_second(current_time, asr_date) // 60) % 60) if current_time >= asr_date and current_time <= maghrib_date: remaining_hours = int( self.get_remaining_second(current_time, maghrib_date) // 3600) remaining_minutes = int( (self.get_remaining_second(current_time, maghrib_date) // 60) % 60) if current_time >= maghrib_date and current_time <= isya_date: remaining_hours = int( self.get_remaining_second(current_time, isya_date) // 3600) remaining_minutes = int( (self.get_remaining_second(current_time, isya_date) // 60) % 60) # if current_time >= isya_date and current_time <= subh_date: if current_time >= isya_date or current_time <= subh_date: subh_date = subh_date + datetime.timedelta(days=1) remaining_hours = int( self.get_remaining_second(current_time, subh_date) // 3600) remaining_minutes = int( (self.get_remaining_second(current_time, subh_date) // 60) % 60) remaining_str = f'{remaining_hours} hours and ' if ( remaining_hours) else '' remaining_str += f'{remaining_minutes} minutes to go' self.ui.lblRemaining.setText(remaining_str) def do_calculate(self): while True: time.sleep(1) # update time every time self.init_times_new() self.showTimes() self.show_remaining_time() # Check Azan Time current_time = datetime.datetime.now() now = current_time.strftime("%H:%M") if now == self.time_array['fajr'].strip( ) or now == self.time_array['dhuhr'].strip( ) or now == self.time_array['asr'].strip( ) or now == self.time_array['maghrib'].strip( ) or now == self.time_array['isha'].strip(): self.playAzan() # self.show_current_prayer_time() self.show_prayer_time_info() time.sleep(60) # ----------------------------------------------------------------------- # checking for jumah notification # friday is 4 if current_time.weekday() == 4: if self.enable_jumah_notification: # dhuhr dhuhr_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d') ) + ' ' + self.time_array['dhuhr'].strip() + ':00' dhuhr_date = datetime.datetime.strptime( dhuhr_date_str, '%Y/%m/%d %H:%M:%S') if self.get_remaining_time(current_time, dhuhr_date) == int( self.before_jumah_time): self.playNotif('Jum`ah') time.sleep(75) # checking notification before pray time if self.enable_notification_before: # subh subh_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d') ) + ' ' + self.time_array['fajr'].strip() + ':00' subh_date = datetime.datetime.strptime(subh_date_str, '%Y/%m/%d %H:%M:%S') # dhuhr dhuhr_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d') ) + ' ' + self.time_array['dhuhr'].strip() + ':00' dhuhr_date = datetime.datetime.strptime( dhuhr_date_str, '%Y/%m/%d %H:%M:%S') # asr asr_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d') ) + ' ' + self.time_array['asr'].strip() + ':00' asr_date = datetime.datetime.strptime(asr_date_str, '%Y/%m/%d %H:%M:%S') # asr maghrib_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d') ) + ' ' + self.time_array['maghrib'].strip() + ':00' maghrib_date = datetime.datetime.strptime( maghrib_date_str, '%Y/%m/%d %H:%M:%S') # isya isya_date_str = str(current_time.year) + '/' + str( current_time.strftime('%m')) + '/' + str( current_time.strftime('%d') ) + ' ' + self.time_array['isha'].strip() + ':00' isya_date = datetime.datetime.strptime(isya_date_str, '%Y/%m/%d %H:%M:%S') if self.get_remaining_time(current_time, subh_date) == int( self.before_pray_time): self.playNotif('Fajr') time.sleep(75) if self.get_remaining_time(current_time, dhuhr_date) == int( self.before_pray_time): self.playNotif('Dhuhr') time.sleep(75) if self.get_remaining_time(current_time, asr_date) == int( self.before_pray_time): self.playNotif('Asr') time.sleep(75) if self.get_remaining_time(current_time, maghrib_date) == int( self.before_pray_time): self.playNotif('Maghrib') time.sleep(75) if self.get_remaining_time(current_time, isya_date) == int( self.before_pray_time): self.playNotif('Isya') time.sleep(75) def get_remaining_second(self, time_1, time_2): time_delta = (time_2 - time_1) return time_delta.total_seconds() # in seconds def get_remaining_time(self, time_1, time_2): # in minutes return round(self.get_remaining_second(time_1, time_2) / 60) def runningme(self): # do some stuff self.show_prayer_time_info() self.docalc.start() def showImageAzan(self): image_dir = self.current_directory + '/images' filename = random.choice(os.listdir(image_dir)) image_path = image_dir + '/' + filename im = Image.open(image_path) im_width, im_height = im.size im_width -= 75 im_height -= 75 self.azanDialog.resize(im_width, im_height) im_width -= 10 im_height -= 10 print('showing azan dialog') btnDialog = self.azanDialog.children()[0] btnDialog.setGeometry(5, 5, im_width, im_height) styleBg = "border-image: url('" + image_path + \ "') 0 0 0 0 stretch stretch;" btnDialog.setStyleSheet(styleBg) self.azanDialog.show() def stopAzan(self): try: self.azanplay.stop() except AttributeError as ae: print(ae) def stopNotif(self): self.notifplay.stop() def playAzan(self): self.threadAzan = threading.Thread(target=self._playAzan, name="Play Azan") self.threadAzan.start() self.showImageAzan() def playNotif(self, time_string): notification.notify(title="It's time to Shalat.", message=str(self.before_pray_time) + " minutes before " + time_string + ' prayer time.', timeout=10) self.threadNotif = threading.Thread(target=self._playNotif, name="Play Notif") self.threadNotif.start() def _playAzan(self): try: self.azanplay.stop() self.azanplay = AzanPlay(self.default_azan_wav) self.azanplay.play() except AttributeError: self.azanplay = AzanPlay(self.default_azan_wav) self.azanplay.play() def _playNotif(self): try: self.notifplay.stop() self.notifplay = AzanPlay(self.default_notif_wav) self.notifplay.play() except AttributeError: self.notifplay = AzanPlay(self.default_notif_wav) self.notifplay.play() def showTimes(self): # show label current date name self.ui.lblTodayName.setText('Today / ' + datetime.datetime.now().strftime('%A')) self.ui.lblTodayDate.setText( datetime.datetime.now().strftime('%d %B %Y / %H:%M')) self.ui.lblSunset.setText(self.time_array['sunset']) self.ui.lblSunrise.setText(self.time_array['sunrise']) # using new calculation module self.ui.txFajr.setText(self.time_array['fajr'].strip()) self.ui.txDhuhr.setText(self.time_array['dhuhr'].strip()) self.ui.txAshr.setText(self.time_array['asr'].strip()) self.ui.txMaghrib.setText(self.time_array['maghrib'].strip()) self.ui.txIsya.setText(self.time_array['isha'].strip()) def init_times_new(self): today = datetime.date.today() PT = PrayTimes('MWL') times = PT.get_times(today, (float(self.latitude), float(self.longitude)), float(self.utc_offset)) self.time_array = times def open_setting(self): # init database self.db = TinyDB('ayodb.json') self.TinyData = TinyQuery() # opening app setting try: setting_lines = self.db.search(self.TinyData.code == 'setting')[0] except IndexError: self.init_db() setting_lines = self.db.search(self.TinyData.code == 'setting')[0] # fileob = open(self.setting_file, 'r') # setting_lines = fileob.readlines() try: # open in tray self.open_in_tray = setting_lines['open_in_tray'] or 'False' # latitude self.latitude = setting_lines['latitude'] or -7.502814765426055 # longitude self.longitude = setting_lines['longitude'] or 112.71057820736571 # utc self.utc_offset = setting_lines['utc_offset'] or 7 # calculation method self.calculation_method_index = int( setting_lines['calculation_method_index']) or 0 self.calculation_method = self.calculation_method_array[int( self.calculation_method_index)] # time format self.time_format = setting_lines['time_format'] or '24h' # mathhab self.mathhab_index = int(setting_lines['mathhab_index']) or 0 self.mathhab = self.mathhab_array[int(self.mathhab_index)] self.before_pray_time = setting_lines['before_pray_time'] self.enable_notification_before = strtobool( setting_lines['enable_notification_before']) self.enable_jumah_notification = strtobool( setting_lines['enable_jumah_notification']) self.before_jumah_time = setting_lines['before_jumah_time'] if self.open_in_tray == 'True': # self.hide() self.ui.ckStartTray.setChecked(True) # fileob.close() self.ui.txLat.setText(str(self.latitude)) self.ui.txLong.setText(str(self.longitude)) self.ui.txUtc.setText(str(self.utc_offset)) self.ui.txBeforeTime.setText(str(self.before_pray_time)) self.ui.cbMethod.setCurrentIndex(self.calculation_method_index) self.ui.cbMathhab.setCurrentIndex(self.mathhab_index) self.ui.ckNotification.setChecked(self.enable_notification_before) self.ui.txBeforeJumah.setText(str(self.before_jumah_time)) self.ui.ckJumah.setChecked(self.enable_jumah_notification) except KeyError: self.init_db() self.open_setting() def init_db(self): # delete first data self.db.remove(self.TinyData.code == 'setting') item = { 'code': 'setting', 'open_in_tray': 'False', 'latitude': -7.502814765426055, 'longitude': 112.71057820736571, 'utc_offset': 7, 'calculation_method_index': 0, 'calculation_method': '', 'time_format': '24h', 'mathhab_index': 0, 'mathhab': '', 'enable_notification_before': "False", 'before_pray_time': 0, 'enable_jumah_notification': "False", 'before_jumah_time': 0, } self.db.insert(item) # def show_current_prayer_time(self): # current_time = datetime.datetime.now() def closeEvent(self, event): self.qtray.hide() self.qtray.deleteLater() self.deleteLater() def reformat_ui(self): self.ui.txBeforeTime.hide() self.ui.label_7.hide() self.ui.txBeforeJumah.hide() self.ui.label_8.hide() icon = QIcon() icon.addFile(self.current_directory + u"/icon/masjid.xpm", QSize(), QIcon.Normal, QIcon.Off) # MainWindow.setWindowIcon(icon) self.ui.centralwidget.setWindowIcon(icon) self.ui.frameSetting.setGeometry(QRect(-1, -1, 322, 461)) self.ui.frameSetting.setStyleSheet(u"#frameSetting{\n" " background-image:url('" + self.current_directory + "/icon/bg6-3.jpg');\n" " background-position:center;\n" "}\n" ".QLabel{\n" "color:white;}\n") self.ui.frameDashboardUpper.setGeometry(QRect(-10, -1, 391, 271)) self.ui.frameDashboardUpper.setStyleSheet( u"#frameDashboardUpper{\n" " background-image:url('" + self.current_directory + "/icon/bg6-3.jpg');\n" " background-position:center;\n" "}") self.ui.lblLocation.setStyleSheet(u"#lblLocation{\n" " color:white;\n" " background-image:url('" + self.current_directory + "/icon/location_on-24px.svg');\n" "background-position:center;\n" "}") self.ui.label_10.setStyleSheet(u".QLabel{\n" " background-image:url('" + self.current_directory + "/icon/noun_Sea Sunset_395675.svg');\n" " background-position:center;\n" " background-repeat:no-repeat;\n" "}") self.ui.label_16.setStyleSheet(u".QLabel{\n" " background-image:url('" + self.current_directory + "/icon/noun_Sunrise _395417.svg');\n" " background-position:center;\n" " background-repeat:no-repeat;\n" "}") self.ui.frame_4.setStyleSheet(u"#frame_4{\n" " background-image:url('" + self.current_directory + "/icon/today-24px.svg');\n" " background-repeat:no-repeat;\n" " background-position:center;\n" "}") self.ui.label_11.setStyleSheet(u"background-image:url('" + self.current_directory + "/icon/alarm_on-24px.svg');\n" "background-repeat:no-repeat;\n" "background-position:right center;") self.ui.label_14.setStyleSheet(u"background-image:url('" + self.current_directory + "/icon/alarm_on-24px.svg');\n" "background-repeat:no-repeat;\n" "background-position:right center;") self.ui.label_17.setStyleSheet(u"background-image:url('" + self.current_directory + "/icon/alarm_on-24px.svg');\n" "background-repeat:no-repeat;\n" "background-position:right center;") self.ui.label_20.setStyleSheet(u"background-image:url('" + self.current_directory + "/icon/alarm_on-24px.svg');\n" "background-repeat:no-repeat;\n" "background-position:right center;") self.ui.label_23.setStyleSheet(u"background-image:url('" + self.current_directory + "/icon/alarm_on-24px.svg');\n" "background-repeat:no-repeat;\n" "background-position:right center;") self.ui.lblIconSetting.setStyleSheet( u"background-image:url('" + self.current_directory + "/icon/settings-24px-white.svg');\n" "background-repeat:no-repeat;\n" "background-position:left center;") icon1 = QIcon() icon1.addFile(self.current_directory + u"/icon/date_range-24px.svg", QSize(), QIcon.Normal, QIcon.On) self.ui.btnTimeTable.setIcon(icon1) self.ui.btnTimeTable.setCursor(QCursor(Qt.PointingHandCursor)) icon2 = QIcon() icon2.addFile(self.current_directory + u"/icon/settings-24px.svg", QSize(), QIcon.Normal, QIcon.On) self.ui.btnSetting.setIcon(icon2) self.ui.btnSetting.setCursor(QCursor(Qt.PointingHandCursor)) icon3 = QIcon() icon3.addFile(self.current_directory + u"/icon/hide_source-24px.svg", QSize(), QIcon.Normal, QIcon.On) self.ui.btnHide.setIcon(icon3) self.ui.btnHide.setCursor(QCursor(Qt.PointingHandCursor)) icon4 = QIcon() icon4.addFile(self.current_directory + u"/icon/exit_to_app-24px.svg", QSize(), QIcon.Normal, QIcon.On) self.ui.btnExit.setIcon(icon4) self.ui.btnExit.setCursor(QCursor(Qt.PointingHandCursor)) self.ui.containerSetting.setStyleSheet( u".QLineEdit{\n" " border:none;\n" " border-radius:0px;\n" " background-color:whitesmoke;\n" " border-bottom:1px solid gray;\n" "}\n" ".QLineEdit:focus{\n" " background-color:rgb(255, 186, 97);\n" "}\n" "#containerSetting{\n" " background-color: rgb(255, 255, 255);\n" " border-radius:10;\n" " border-style:outer;\n" " border:1px solid #cbcbcb;\n" "}\n" ".QLabel{\n" " color:black;\n" "}\n" ".QPushButton{\n" " border-radius:2px;\n" " background-color:rgb(0, 85, 127);\n" " color:#fff;\n" "}\n" ".QPushButton:hover{\n" " background-color:rgb(0, 135, 201);\n" "}\n" ".QCheckBox{\n" " color:black;\n" "}\n" "\n" ".QComboBox{\n" "border:none;\n" " border:none;\n" " border-radius:0px;\n" " background-color:whitesmoke;\n" " border-bottom:1px solid gray;\n" " selection-color: black;\n" " selection-background-color: rgb(255, 186, 97);\n" "}\n" ".QComboBox:focus{\n" " background-color:rgb(255, 186, 97);\n" "}\n" "\n" ".QComboBox::drop-down:button{\n" " background-color: rgb(234,234,234);\n" " width:25px;\n" "background-image:url('" + self.current_directory + "/icon/expand_more-24px.svg')\n" "}")
class GUI(QMainWindow, Ui_MainWindow, QtStyleTools): ICON: QIcon TITLE: str = "Contour ShuttleXpress" WHEEL_MAP: Dict[int, QObject] BUTTON_MAP: List[QObject] LOG_COLORS = { logging.DEBUG: 'lightgray', logging.INFO: 'white', logging.WARNING: 'orange', logging.ERROR: 'red', logging.CRITICAL: 'purple', } def __init__(self) -> None: super().__init__() # self.load_ui() # Broken # Use pyside-uic form.ui > gui.py to generate the UI instead self.setupUi(self) # Hide ASAP to avoid window flash self.about_widget.setHidden(True) self.plugins_widget.setHidden(True) self.config_widget.setHidden(True) self.log_widget.setHidden(True) h = LogHandler(self.append_log) fs = '%(levelname)-8s %(message)s' formatter = logging.Formatter(fs) h.setFormatter(formatter) logging.getLogger().addHandler(h) self.ICON = QIcon('images/icon.png') self.WHEEL_MAP = { -7: self.wheel_neg7, -6: self.wheel_neg6, -5: self.wheel_neg5, -4: self.wheel_neg4, -3: self.wheel_neg3, -2: self.wheel_neg2, -1: self.wheel_neg1, 0: self.wheel_cent0, 1: self.wheel_pos1, 2: self.wheel_pos2, 3: self.wheel_pos3, 4: self.wheel_pos4, 5: self.wheel_pos5, 6: self.wheel_pos6, 7: self.wheel_pos7, } self.BUTTON_MAP = [None, self.button_1, self.button_2, self.button_3, self.button_4, self.button_5] self.setWindowIcon(self.ICON) self.setWindowTitle(self.TITLE) #self.set_ms_windows_icon() extra = { # Button colors 'danger': '#dc3545', 'warning': '#ffc107', 'success': '#17a2b8', # Font 'font_family': 'Roboto', } self.apply_stylesheet(self, theme='dark_red.xml', extra=extra) # self.setWindowFlags(self.windowFlags() | Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint) # self.statusbar.setSizeGripEnabled(False) self.update_status_bar("Connecting...") self.show() if QSystemTrayIcon.isSystemTrayAvailable(): self.systray = QSystemTrayIcon() self.systray.setIcon(self.ICON) systray_menu = QMenu(title=self.TITLE, parent=self) quit_action = QAction("&Quit", self) systray_menu.addAction(quit_action) quit_action.triggered.connect(self.close) self.systray.setContextMenu(systray_menu) self.systray.setVisible(True) self.systray.activated.connect(self.handle_systray_activation) shuttle_signals.data.connect(self.handle_events) self.shuttle_worker = ShuttleWorker() self.shuttle_worker.start() self.shuttle_worker.finished.connect(self.shuttle_worker.quit) self.about_text.setMarkdown(f""" Contour ShuttleXpress ===================== A multiplatform userland driver, configuration editor, event manager & generator for Contour ShuttleXpress. Version: `{__version__}` Source: `{__repo__}` Running on Python v{python_version()} Legal notice ------------ ### License Copyright 2021-2022 Raphaël Doursenaud This software is released under the terms of the GNU General Public License, version 3.0 or later (GPL-3.0-or-later). ### Dependencies & License Acknowledgment **Python** Used under the terms of the PSF License Agreement. **libusb hidapi** Copyright Alan Ott, Signal 11 Software. Used under the terms of the GNU General Public License, version 3.0 (GPL-3.0). **Trezor cython-hidapi** Copyright Pavol Rusnak, SatoshiLabs. Used under the terms of the GNU General Public License, version 3.0 (GPL-3.0). **Qt PySide6** Used under the terms of the GNU Lesser General Public License v3.0 (LGPL-3.0). **UN-GCPDS Qt-Material** Used under the BSD-2-Clause License. **Material Design Icons** Used under the Pictogrammers Free License. ### Trademarks Contour, ShuttleXpress and ShuttlePro are trademarks of Contour Innovations LLC in the United States of America. These are not registered or active trademarks in the European Union and France where I reside. """) self.about_button.clicked.connect(self.toggle_about_vis) self.plug_button.clicked.connect(self.toggle_plugins_vis) self.conf_button.clicked.connect(self.toggle_config_vis) self.log_button.clicked.connect(self.toggle_log_vis) self.log_clear_button.clicked.connect(self.clear_log) # def load_ui(self): # loader = QUiLoader() # path = os.path.join(os.path.dirname(__file__), "mainwindow.ui") # ui_file = QFile(path) # ui_file.open(QFile.ReadOnly) # print(ui_file.readAll()) # loader.load(ui_file, self) # ui_file.close() def handle_systray_activation(self, reason: QSystemTrayIcon.ActivationReason) -> None: if reason is QSystemTrayIcon.ActivationReason.Trigger: self.toggle_main_window_visibility() def toggle_main_window_visibility(self) -> None: self.hide() if self.isVisible() else self.show() def update_status_bar(self, message: str) -> None: self.statusbar.showMessage(message) def toggle_about_vis(self) -> None: self.about_widget.setVisible(self.about_widget.isHidden()) def toggle_config_vis(self) -> None: self.config_widget.setVisible(self.config_widget.isHidden()) def toggle_plugins_vis(self) -> None: self.plugins_widget.setVisible(self.plugins_widget.isHidden()) def toggle_log_vis(self) -> None: self.log_widget.setVisible(self.log_widget.isHidden()) @Slot(str, logging.LogRecord) def append_log(self, message, record): color = self.LOG_COLORS.get(record.levelno, 'black') s = f'<pre><font color="{color}">{message}</font></pre>' self.log_text.appendHtml(s) def clear_log(self) -> None: self.log_text.clear() def handle_events(self, event: Event) -> None: self.update_status_bar(event.desc) if isinstance(event, ConnectionEvent): self.usb_status.setChecked(True) elif isinstance(event, DisconnectionEvent): self.usb_status.setChecked(False) # self.shuttle_worker.stop() elif isinstance(event, RotaryEvent): self.handle_rotary_event(event) elif isinstance(event, ButtonEvent): self.handle_button(event.element) def handle_rotary_event(self, rotary_event: RotaryEvent) -> None: if type(rotary_event.element) is Wheel: self.handle_wheel(rotary_event.element, rotary_event.direction, rotary_event.value) elif type(rotary_event.element) is Dial: self.handle_dial(rotary_event.element, rotary_event.direction, rotary_event.value) def handle_wheel(self, wheel: Wheel, direction: bool, change: int) -> None: self.WHEEL_MAP[wheel.pos].setChecked(True) def handle_dial(self, dial: Dial, direction: bool, change: int) -> None: self.dial.setValue(self.dial.value() + change) def handle_button(self, button: Button) -> None: self.BUTTON_MAP[button.num].setChecked(button.push) def closeEvent(self, event: PySide6.QtGui.QCloseEvent) -> None: self.shuttle_worker.stop() @staticmethod def set_ms_windows_icon() -> None: """ Force Microsoft Windows to properly display the application icon in the task bar Workaround from: https://stackoverflow.com/a/27872625 """ try: import ctypes myappid = u'eu.ematech.contour.shuttlexpress' # arbitrary string ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(myappid) except AttributeError as e: logging.warning(f"{e}") pass @staticmethod def set_mac_os_title() -> None: """ Force Mac OS X to properly display the app name in the title bar using PyObjC Workaround from: https://stackoverflow.com/a/54755290 """ try: from Foundation import NSBundle bundle = NSBundle.mainBundle() if bundle: app_name = GUI.TITLE app_info = bundle.localizedInfoDictionary() or bundle.infoDictionary() if app_info: app_info['CFBundleName'] = app_name except ImportError as e: logging.warning(f"{e}") pass