├── .gitignore
├── README.md
├── res
├── mail-black.png
├── mail.png
├── popup.wav
├── popup2.wav
└── thunderbird.png
├── settings.ini
├── tbtray.py
├── tbtrayui.py
└── tbtrayui.ui
/.gitignore:
--------------------------------------------------------------------------------
1 | log.txt
2 | .idea/
3 | .vscode/
4 | __pycache__/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TBtray #
2 |
3 | ## A Thunderbird tray addon for linux built with PyQt5 ##
4 |
5 | I only built it because we did'nt have any TB tray integration after Thunderbird 60+.
6 | Keep in mind im just a hobbyist, Im doing this to try and learn a little about using git and github.
7 |
8 |
9 | ## Installation ##
10 |
11 | you will need to install these packages on your system first
12 |
13 | python 3.6+
14 |
15 | xdotool
16 | wmctrl [these 2 control the window manager]
17 | python3-pyqt5.qtmultimedia (or qt5-multimedia) [One of these are for the sound]
18 |
19 | Then clone the repo
20 | git clone https://github.com/JackDinn/tbtray.git
21 |
22 | Run with
23 | tbtray/tbtray.py
24 |
25 |
26 |
27 | After installing and running TBtray select your INBOX.msf files for your accounts.
28 | Find (or manually enter) the path to your INBOX.msf files in the top bar of the settings and click "add" to put them
29 | into your profile list box.
30 |
31 | example of INBOX.msf :-
32 | /home/user/.thunderbird/tzvg3gbn.default/ImapMail/imap.gmail.com/INBOX.msf
33 |
34 |
35 | #### **_You can not use the unified (Smart-mail) inbox._** ####
36 | This does not mean you can not use the unified mail box with Thunderbird, just that you will need to setup each account individually in TBtray.
37 |
38 |
39 | #### Features :- ####
40 |
41 | * Minimize to tray
42 | * Click tray icon to show/hide TB
43 | * Show unread count on tray icon
44 | * set icons (one for default (no unread), another for the notification
45 | * Allow TBtray to take over the popup notification & sound from TB
46 | * You can also click on the popup notification to show the TB window
47 | * You can also use the shortcut launch icon to hide/show TB (if you have made one)
48 | * Set opacity of popup
49 | * popup duration control
50 | * Popup option to Show favicons alongside individual emails (these are scraped once & then cached locally)
51 | * Very low idle CPU & Memory.
52 |
53 |
54 |
55 | 
56 |
57 | 
58 |
59 | 
60 |
61 |
62 | ### General usage ###
63 | TBtray executes TB so i advise creating a launcher that runs TBtray to replace your TB launcher.
64 |
65 | It can sometimes take upto 1 second for TBtray to hide TB, this is unavoidable.
66 |
67 | You can close both TBtray and TB together via the tray icon.
68 |
69 | You can run TBtray after TB is already running and it will work but you may need to "synchronize" it by clicking the tray icon.
70 |
71 | I can not figure a way to intercept the TB close signal so i can not have TBtray minimize TB when you click close on TB. The best i can do is to close both TB and TBtray if you close TB.
72 |
73 | TBtray (PyQt5) does not seem to work well on Wayland.
74 |
75 |
76 | ### Removal of TBtray ###
77 | just delete the tbtray folder and the settings folder found at ~/.config/tbtray
78 |
--------------------------------------------------------------------------------
/res/mail-black.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JackDinn/tbtray/21ce358b46ffcea9d42838481f3646e09df5dcdc/res/mail-black.png
--------------------------------------------------------------------------------
/res/mail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JackDinn/tbtray/21ce358b46ffcea9d42838481f3646e09df5dcdc/res/mail.png
--------------------------------------------------------------------------------
/res/popup.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JackDinn/tbtray/21ce358b46ffcea9d42838481f3646e09df5dcdc/res/popup.wav
--------------------------------------------------------------------------------
/res/popup2.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JackDinn/tbtray/21ce358b46ffcea9d42838481f3646e09df5dcdc/res/popup2.wav
--------------------------------------------------------------------------------
/res/thunderbird.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/JackDinn/tbtray/21ce358b46ffcea9d42838481f3646e09df5dcdc/res/thunderbird.png
--------------------------------------------------------------------------------
/settings.ini:
--------------------------------------------------------------------------------
1 | [popup]
2 | opacity = 100
3 | on = 1
4 | soundpath = res/popup.wav
5 | soundon = 1
6 | x = 1875
7 | favicons = 1
8 | duration = 10
9 | fixedwidth = 1
10 | top = 1
11 |
12 | [ticks]
13 | minimizetotray = 1
14 | showcount = 1
15 |
16 | [icons]
17 | default = res/thunderbird.png
18 | notify = res/mail.png
19 | colour = #ffffff
20 |
21 | [profiles]
22 |
23 |
24 |
--------------------------------------------------------------------------------
/tbtray.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python3
2 | import configparser
3 | import getpass
4 | import mailbox
5 | import os
6 | import re
7 | import subprocess
8 | import sys
9 | import urllib.request
10 | from email.header import decode_header, make_header
11 | from pathlib import Path
12 | from shutil import copyfile
13 | from time import strftime
14 |
15 | from PyQt5 import QtCore, QtGui, QtWidgets
16 | from PyQt5.QtCore import Qt, QTimer
17 | from PyQt5.QtGui import QColor, QFont, QFontMetrics, QPainter, QPixmap
18 | from PyQt5.QtMultimedia import QSound
19 | from PyQt5.QtWidgets import QAction, QColorDialog, QFileDialog, QMenu, QSystemTrayIcon
20 |
21 | import tbtrayui
22 |
23 |
24 | def close():
25 | os.system('pkill thunderbird')
26 | sys.exit(0)
27 |
28 |
29 | def checkvisable():
30 | winname = ' - Mozilla Thunderbird'
31 | try:
32 | # check 3 times because sometimes when there are lots of child windows open it misses the fact that the TB window is actually visible.
33 | for two in range(3):
34 | out = subprocess.run(
35 | ["xdotool", "search", "--all", "--onlyvisible", "--maxdepth", "3", "--limit", "1", "--name", winname],
36 | stdout=subprocess.PIPE).stdout.decode('UTF-8')
37 | log('check is TB window visable ' + out)
38 | if out: return True
39 | except:
40 | pass
41 | return False
42 |
43 |
44 | def log(tex=''):
45 | try:
46 | tail = subprocess.run(["tail", "-n", "500", "log.txt"], stdout=subprocess.PIPE).stdout.decode('UTF-8')
47 | with open('log.txt', 'w+') as xx:
48 | xx.write(tail)
49 | tim = strftime("%y-%m-%d %H:%M:%S")
50 | with open('log.txt', 'a+') as qq:
51 | qq.write(tim + ' ' + tex + '\n')
52 | except:
53 | pass
54 |
55 |
56 | def getfavicon(url):
57 | path = str(Path.home()) + '/.config/tbtray/icons/'
58 | iconpath = path + url + '.ico'
59 | if Path.is_file(Path(iconpath)):
60 | log('Local icon ' + iconpath)
61 | return iconpath
62 | try:
63 | log('favicon URL https://www.google.com/s2/favicons?domain=' + url)
64 | data = urllib.request.urlopen('https://www.google.com/s2/favicons?domain=' + url)
65 | icon = data.read()
66 | if icon == b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f' \
67 | b'\xf3\xffa\x00\x00\x01\xb3IDATx\x01b\xf8\xff\xff?E\x18\xce\xf0\x8b\x9c\xc7\xe9\xe8;C\xb6\xaai[PI\xed\xe6\xbd\xc9\xb9' \
68 | b'\xab>\x86\xc4\x03\xe8\xa6\x86\xed\xda\x82(\xf81\xefc\xde\xf4b\x18\xdb\xb6\xedd\x1a\xdb\xb6m\xdb\xb6m\';]\x83\xee\xd5W{' \
69 | b'\xad\xe3\xaa\xda\xaaS\xf8\x83\xc3\xd2\xb9\xf2\xc1+\xb8\xa9\'\x80}\x03\x06X\xce\x13\xe4\xff\xaa\xac\x7f.~\xf5\xf1\x16N\x15' \
70 | b'\xab1\xc9\xdd\xd7E\xb5\xb3\x94S1E\xb8\x0eO\x1fPX\\\xc7\xb5\x99C\xf9\xaa\x8bo]<\xb0\xe0\x08\x01\xa8\xba\xf8\xd6\xc7[\xb9T\x1d' \
71 | b'\xa6d\x0cRm\xfb\nu\x0cn\xd1\xd5\xdd\x1b\xed\x9f<\x10\xe2\xf4\xf2\x85\xf0\x8e\x89\x1c:\xfb\xd4\xc5\x83#\x04P\x9a\x99c\xf9*2\x81' \
72 | b'\x8c\xa3\xa9g\x1d\x87\x10\xf8\xfe!\xea\x1d\xdb\xa5>v\x98:\x94\xad\x82#\x04\xd0\x1fJ\x04\x08d\xf9xx\xfe"\x04\xaex\x9e_=\xa3\xe0' \
73 | b'\xe8\xf6k\xcf\xa0\xc6\x1e!\x80!!;@\xfa\xc8\xbc\x82\x95\xad+\xc2{`\xcd\x1d+\x1e\x84\x80\xda\xb4\xe0gk\xff\x8e\x10\xe8\x13@N\x96' \
74 | b'\x03-@\x00X\x95q\xfe\x8f\x86\x00\x86\xc4K\xe5\x03\xd4\'\x80\xb9@@)\x0b\xb0\xd5\xa1\x05\x92\x03\xcf\xfc\xe0\xe2\x08&\x8cw\xd8\xc6' \
75 | b'\x83\xc6\x10\xc3\xd9\x10%\xbe\xce<&\x16N\x88\x87_x\xcb\xb5G`c\x8f\xbcF%L\x02eC\x0280H`L\xec\xd9\x1a#Z\x95\x1aF\x82\xc3\x98\x17\x0ee' \
76 | b'\x11\xf4\xca\xc9\xb8\x1f\x06\xd9\xaeL\xd7H\xdc\xca\xceL\x04&a\xff\xc35\xc00\r\xec\x9cV4N\x81\x91m\xd7\xc8\x0c\xb2\x8e\x95\xe5' \
77 | b'\x9f\t\xa5\xc1$\xd8\xb3\xca\xa4\xe0\x07\xd3\xfe\x1b(\xc0\x80b{\xaa\x9b\xb7\x078a\xc9L\x14a\x00BM\xf5\xdf\xed\xe70\xb1\x00\x00' \
78 | b'\x00\x00IEND\xaeB`\x82':
79 | os.symlink(os.getcwd() + '/res/thunderbird.png', iconpath)
80 | return 'res/thunderbird.png'
81 | with open(iconpath, "wb") as f:
82 | f.write(icon)
83 | return iconpath
84 | except:
85 | log('Failed google scrape ' + url)
86 | return 'res/thunderbird.png'
87 |
88 |
89 | def checksettings():
90 | my_dir = Path(str(Path.home()) + '/.config/tbtray')
91 | if not my_dir.is_dir(): my_dir.mkdir()
92 | if not Path(str(Path.home()) + '/.config/tbtray/icons').is_dir(): Path.mkdir(
93 | Path(str(Path.home()) + '/.config/tbtray/icons'))
94 | my_file = Path(str(Path.home()) + '/.config/tbtray/settings.ini')
95 | if not my_file.is_file(): copyfile('settings.ini', str(my_file))
96 | config_new = configparser.ConfigParser()
97 | config_old = configparser.ConfigParser()
98 | config_new.read('settings.ini')
99 | config_old.read(str(my_file))
100 | for xx in config_new.sections():
101 | if xx == 'profiles': continue
102 | if not config_old.__contains__(xx):
103 | config_old.add_section(xx)
104 | for hh in config_new[xx]:
105 | if not config_old[xx].__contains__(hh):
106 | config_old[xx][hh] = config_new[xx][hh]
107 | with open(my_file, 'w') as configfile:
108 | config_old.write(configfile)
109 | configfile.close()
110 |
111 |
112 | def readmessage(path):
113 | from_text = []
114 | subject_text = []
115 | date_text = []
116 | messageid_text = []
117 | for gg in path:
118 | try:
119 | if not os.path.isfile(gg): continue
120 | text = subprocess.run(["tail", "-n", "10000", gg], stdout=subprocess.PIPE).stdout.decode('UTF-8', "ignore")
121 | with open('/tmp/tbtraydata', 'w+') as xyz:
122 | xyz.write(text)
123 | fr = mailbox.mbox('/tmp/tbtraydata')
124 | if os.path.isfile('/tmp/tbtraydata'): os.remove('/tmp/tbtraydata')
125 | for q in fr:
126 | try:
127 | messageid_text.append(str(make_header(decode_header(q['message-ID']))))
128 | date_text.append(str(make_header(decode_header(q['date']))))
129 | from_text.append(
130 | str(make_header(decode_header(q['from']))).replace('<', '<').replace('>', '>'))
131 | subject_text.append(str(make_header(decode_header(q['subject']))).replace('\r\n', '
'))
132 | except:
133 | continue
134 | except:
135 | continue
136 | return {'from': from_text, 'subject': subject_text, 'date': date_text, 'messageid': messageid_text}
137 |
138 |
139 | class TextBrowser(QtWidgets.QTextBrowser):
140 | windowid: int
141 |
142 | def __init__(self, parent=None):
143 | super(TextBrowser, self).__init__(parent)
144 | self.windowid = 0
145 | self.INTRAY = False
146 | self.hideme = False
147 | self.height = 100
148 | self.width = 100
149 | self.fixedwidth = True
150 | self.document().contentsChanged.connect(self.sizechange)
151 |
152 | def sizechange(self):
153 | docwidth = 300
154 | docheight = self.document().size().height()
155 | if self.fixedwidth: docwidth = 300
156 | if not self.fixedwidth: docwidth = self.document().size().width()
157 | self.height = int(docheight)
158 | self.width = int(docwidth)
159 | self.setGeometry(5, 5, self.width + 10, self.height + 5)
160 |
161 | def mouseReleaseEvent(self, event):
162 | subprocess.run(["xdotool", "windowmap", self.windowid])
163 | subprocess.run(['wmctrl', '-i', '-r', str(self.windowid), '-b', 'remove,skip_taskbar'])
164 | subprocess.run(["xdotool", "windowactivate", self.windowid])
165 | self.hideme = True
166 | self.INTRAY = True
167 |
168 |
169 | class Popup(QtWidgets.QDialog):
170 |
171 | # noinspection PyArgumentList
172 | def __init__(self):
173 | super(self.__class__, self).__init__()
174 | self.setObjectName("formpopup")
175 | self.setGeometry(1185, 40, 430, 993)
176 | self.setMinimumSize(QtCore.QSize(0, 0))
177 | self.setStatusTip("")
178 | self.setWindowTitle("formpopup")
179 | self.setObjectName('formpopup')
180 | self.textBrowser = TextBrowser(self)
181 | self.textBrowser.setGeometry(5, 5, 150, 100)
182 | self.textBrowser.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
183 | self.textBrowser.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
184 | self.closebutton = QtWidgets.QPushButton(self)
185 | self.closebutton.setText('X')
186 | self.closebutton.setGeometry(404, 8, 20, 20)
187 | self.closebutton.setStyleSheet('color:red;')
188 | self.closebutton.clicked.connect(self.clicked)
189 | self.screenheight = getscreenheight()
190 | self.top = True
191 | self.duration = 10
192 | self.browsertext = ''
193 | self.favicons = True
194 | self.popupon = True
195 | self.sound = QSound("res/popup.wav")
196 | self.soundon = True
197 | self.popup_timer = QTimer(self)
198 | self.popup_timer.setSingleShot(True)
199 | self.popup_timer.timeout.connect(self.timer)
200 | self.popup_timer2 = QTimer(self)
201 | self.popup_timer2.setInterval(1000)
202 | self.popup_timer2.timeout.connect(self.timer2)
203 | self.setWindowFlags(Qt.FramelessWindowHint)
204 | self.setWindowFlags(Qt.X11BypassWindowManagerHint)
205 | self.setWindowOpacity(0.90)
206 | self.xpos = 1485
207 | self.shownmessages = []
208 |
209 | def fire(self, profiles, firstrun=False):
210 | if self.popupon or self.soundon:
211 | popprofiles = []
212 | fileexists = False
213 | for ss in range(len(profiles)):
214 | popprofiles.append(profiles[ss].replace('INBOX.msf', 'INBOX'))
215 | if os.path.isfile(popprofiles[ss]): fileexists = True
216 | if fileexists:
217 | mailinfo = readmessage(popprofiles)
218 | up = 0
219 | for mc in range(len(mailinfo['messageid'])):
220 | if self.shownmessages.__contains__(mailinfo['messageid'][up]):
221 | mailinfo['from'].pop(up)
222 | mailinfo['subject'].pop(up)
223 | mailinfo['date'].pop(up)
224 | mailinfo['messageid'].pop(up)
225 | else:
226 | up += 1
227 | for fr in mailinfo['messageid']: self.shownmessages.append(fr)
228 | if not firstrun and len(mailinfo['messageid']) > 0:
229 | if not self.isVisible():
230 | self.browsertext = ''
231 | for x in range(len(mailinfo['messageid'])):
232 | log('parsed from ' + mailinfo['from'][x - 1])
233 | log('parsed subj ' + mailinfo['subject'][x - 1])
234 | fromx = mailinfo['from'][x - 1]
235 | subject = mailinfo['subject'][x - 1]
236 | if self.favicons:
237 | fromxy = fromx + '&'
238 | log('fromxy ' + fromxy)
239 | fav = re.findall('@\S*?\.?([\w|-]*(\.\w{2,3})?\.\w{2,3})&', fromxy)
240 | if len(fav) > 0:
241 | log('get favicon ' + fav[0][0])
242 | icon = getfavicon(fav[0][0])
243 | else:
244 | icon = 'res/thunderbird.png'
245 | self.browsertext += '
' + subject 246 | else: 247 | self.browsertext += '
' + subject 248 | if self.popupon: self.show() 249 | if self.soundon: self.sound.play() 250 | self.textBrowser.clear() 251 | self.textBrowser.setText(self.browsertext) 252 | if self.top: 253 | self.setGeometry(self.xpos - self.textBrowser.width, 40, self.textBrowser.width + 20, 254 | self.textBrowser.height + 15) 255 | else: 256 | self.setGeometry(self.xpos - self.textBrowser.width, 257 | self.screenheight - 55 - self.textBrowser.height, self.textBrowser.width + 20, 258 | self.textBrowser.height + 15) 259 | self.closebutton.setGeometry(self.textBrowser.width - 4, 8, 20, 20) 260 | self.popup_timer.start(self.duration * 1000) 261 | self.popup_timer2.start() 262 | 263 | def timer2(self): 264 | if self.textBrowser.hideme: 265 | self.popup_timer2.stop() 266 | self.textBrowser.hideme = False 267 | self.hide() 268 | 269 | def timer(self): 270 | self.popup_timer2.stop() 271 | self.hide() 272 | 273 | def clicked(self): 274 | self.popup_timer2.stop() 275 | self.hide() 276 | 277 | 278 | def getscreenheight(): 279 | out = subprocess.run(["xrandr"], stdout=subprocess.PIPE).stdout.decode('UTF-8') 280 | matches = re.findall('\d*x(\d*).*?\*', out) 281 | if matches: 282 | log('get screen height ' + matches[0]) 283 | return int(matches[0]) 284 | else: 285 | log('get screen height ERROR') 286 | return 1080 287 | 288 | 289 | class MainApp(QtWidgets.QDialog, tbtrayui.Ui_Form): 290 | 291 | def __init__(self): 292 | super(self.__class__, self).__init__() 293 | os.system('thunderbird > /dev/null 2>&1 &') 294 | stdout = subprocess.run(["pgrep", "-fc", "tbtray.py"], stdout=subprocess.PIPE).stdout.decode('UTF-8') 295 | if int(stdout) > 1: 296 | bob = open('/tmp/tbpassover', 'x') 297 | bob.close() 298 | log('make tmp passover file & close') 299 | sys.exit(0) 300 | os.chdir(os.path.dirname(os.path.realpath(__file__))) 301 | self.my_settings_file = Path(str(Path.home()) + '/.config/tbtray/settings.ini') 302 | log('') 303 | log('TBtray started ################################################ ') 304 | self.matches = 0 305 | self.lastmtime = 0 306 | self.timetriggercheck = QTimer(self) 307 | self.tray_icon = QSystemTrayIcon(self) 308 | self.INTRAY = False 309 | self.windowid = 0 310 | self.setupUi(self) 311 | self.profiles = [] 312 | self.badprofile = True 313 | self.defaulticon = self.lineedit_defulticon.text() 314 | self.notifyicon = self.lineedit_notifyicon.text() 315 | checksettings() 316 | config = configparser.ConfigParser() 317 | config.read(self.my_settings_file) 318 | self.radioButton_top.setChecked(bool(int(config['popup']['top']))) 319 | self.radioButton_bottom.setChecked(not bool(int(config['popup']['top']))) 320 | self.checkBox_fixedwidth.setChecked(bool(int(config['popup']['fixedwidth']))) 321 | self.spinBox_displaytime.setValue(int(config['popup']['duration'])) 322 | self.checkBox_favicons.setChecked(bool(int(config['popup']['favicons']))) 323 | self.horizontalSlider_opacity.setValue(int(config['popup']['opacity'])) 324 | self.checkBox_popup.setChecked(bool(int(config['popup']['on']))) 325 | self.lineEdit_notifysound.setText(config['popup']['soundpath']) 326 | self.checkBox_notifysound.setChecked(bool(int(config['popup']['soundon']))) 327 | self.spinBox_xpos.setValue(int(config['popup']['x'])) 328 | self.colour = config['icons']['colour'] 329 | self.colour_pre = config['icons']['colour'] 330 | self.label_colour.setStyleSheet('color: ' + self.colour) 331 | self.checkbox_showcount.setChecked(bool(int(config['ticks']['showcount']))) 332 | self.checkbox_minimizetotray.setChecked(bool(int(config['ticks']['minimizetotray']))) 333 | self.defaulticon = config['icons']['default'] 334 | self.lineedit_defulticon.setText(config['icons']['default']) 335 | self.notifyicon = config['icons']['notify'] 336 | self.lineedit_notifyicon.setText(config['icons']['notify']) 337 | for value in config['profiles']: 338 | self.profiles.append(config['profiles'][str(value)]) 339 | self.listWidget.addItem(config['profiles'][str(value)]) 340 | self.testforprofile() 341 | self.popup = Popup() 342 | self.popup_test = Popup() 343 | self.actionsetup() 344 | self.timersetup() 345 | 346 | def actionsetup(self): 347 | self.label_accountwarrning.setText('') 348 | self.popup.top = self.radioButton_top.isChecked() 349 | self.popup.textBrowser.fixedwidth = self.checkBox_fixedwidth.isChecked() 350 | if self.popup.textBrowser.fixedwidth: self.popup.textBrowser.setLineWrapMode(QtWidgets.QTextBrowser.WidgetWidth) 351 | if not self.popup.textBrowser.fixedwidth: self.popup.textBrowser.setLineWrapMode(QtWidgets.QTextBrowser.NoWrap) 352 | self.popup.duration = self.spinBox_displaytime.value() 353 | self.popup.favicons = self.checkBox_favicons.isChecked() 354 | self.popup.setWindowOpacity(float(self.horizontalSlider_opacity.value() / 100)) 355 | self.popup.xpos = self.spinBox_xpos.value() 356 | self.popup.popupon = self.checkBox_popup.isChecked() 357 | self.popup.soundon = self.checkBox_notifysound.isChecked() 358 | self.popup.sound = QSound(self.lineEdit_notifysound.text()) 359 | self.tray_icon.setIcon(QtGui.QIcon(self.defaulticon)) 360 | self.tray_icon.setToolTip('TBtray') 361 | action_hideshow = QAction("Hide/Show", self) 362 | action_settings = QAction("Settings", self) 363 | action = QAction("Exit", self) 364 | tray_menu = QMenu() 365 | tray_menu.addAction(action_hideshow) 366 | tray_menu.addAction(action_settings) 367 | tray_menu.addAction(action) 368 | self.tray_icon.setContextMenu(tray_menu) 369 | action.triggered.connect(close) 370 | action_hideshow.triggered.connect(self.iconmenushowhide) 371 | action_settings.triggered.connect(self.settings) 372 | self.toolButton_firepopup.clicked.connect(self.func_toolbutton_firepopup) 373 | self.toolButton_notifysound.clicked.connect(self.func_toolbutton_notifysound) 374 | self.tray_icon.activated.connect(self.iconclick) 375 | self.pushButton_cancel.clicked.connect(self.cancel) 376 | self.pushButton_ok.clicked.connect(self.ok) 377 | self.toolButton_profilepath.clicked.connect(self.selectfile) 378 | self.pushButton_add.clicked.connect(self.func_pushbutton_add) 379 | self.pushButton_remove.clicked.connect(self.func_pushbutton_remove) 380 | self.checkbox_minimizetotray.clicked.connect(self.func_minimizetotrayclicked) 381 | self.toolButton_defaulticon.clicked.connect(self.func_defaulticon) 382 | self.toolButton_notifyicon.clicked.connect(self.func_notifyicon) 383 | self.pushButton_colourpicker.clicked.connect(self.func_colourpicker) 384 | self.tray_icon.show() 385 | if self.badprofile: self.tray_icon.showMessage('TBtray Profile Warning', 'Please setup account profiles', 386 | QSystemTrayIcon.Critical) 387 | self.popup.fire(self.profiles, True) 388 | 389 | def func_toolbutton_firepopup(self): 390 | self.popup_test.textBrowser.windowid = self.windowid 391 | if not self.popup_test.isVisible(): 392 | self.popup_test.browsertext = '' 393 | icon = 'res/thunderbird.png' 394 | self.popup_test.sound = QSound(self.lineEdit_notifysound.text()) 395 | if self.checkBox_favicons.isChecked(): 396 | self.popup_test.browsertext += '
This is a test message. Lorem ipsum dolor sit amet, vivamus platea faucibus sed per penatibus.' 397 | else: 398 | self.popup_test.browsertext += '
This is a test message. Lorem ipsum dolor sit amet, vivamus platea faucibus sed per penatibus.'
399 | if self.checkBox_popup.isChecked(): self.popup_test.show()
400 | self.popup_test.setWindowOpacity(float(self.horizontalSlider_opacity.value() / 100))
401 | self.popup_test.textBrowser.fixedwidth = self.checkBox_fixedwidth.isChecked()
402 | if self.popup_test.textBrowser.fixedwidth: self.popup_test.textBrowser.setLineWrapMode(
403 | QtWidgets.QTextBrowser.WidgetWidth)
404 | if not self.popup_test.textBrowser.fixedwidth: self.popup_test.textBrowser.setLineWrapMode(
405 | QtWidgets.QTextBrowser.NoWrap)
406 | self.popup_test.textBrowser.clear()
407 | self.popup_test.textBrowser.setText(self.popup_test.browsertext)
408 | if self.radioButton_top.isChecked():
409 | self.popup_test.setGeometry(self.spinBox_xpos.value() - self.popup_test.textBrowser.width, 40,
410 | self.popup_test.textBrowser.width + 20, self.popup_test.textBrowser.height + 15)
411 | else:
412 | self.popup_test.setGeometry(self.spinBox_xpos.value() - self.popup_test.textBrowser.width,
413 | self.popup_test.screenheight - 55 - self.popup_test.textBrowser.height,
414 | self.popup_test.textBrowser.width + 20, self.popup_test.textBrowser.height + 15)
415 | self.popup_test.closebutton.setGeometry(self.popup_test.textBrowser.width - 4, 8, 20, 20)
416 | self.popup_test.popup_timer.start(self.spinBox_displaytime.value() * 1000)
417 | self.popup_test.popup_timer2.start()
418 | if self.checkBox_notifysound.isChecked(): self.popup_test.sound.play()
419 |
420 | def func_toolbutton_notifysound(self):
421 | x = QFileDialog.getOpenFileName(self, 'Select Notify Sound File', '/home/' + getpass.getuser())[0]
422 | if x: self.lineEdit_notifysound.setText(x)
423 |
424 | def func_colourpicker(self):
425 | x = QColorDialog.getColor(QColor(self.colour))
426 | if x.isValid():
427 | self.label_colour.setStyleSheet('color: ' + x.name())
428 | self.colour_pre = x.name()
429 |
430 | def func_defaulticon(self):
431 | x = \
432 | QFileDialog.getOpenFileName(self, 'Select Default Icon', 'res/',
433 | "Icons .png .ico .svg (*.png *.ico *.svg)")[0]
434 | if x: self.lineedit_defulticon.setText(x)
435 |
436 | def func_notifyicon(self):
437 | x = QFileDialog.getOpenFileName(self, 'Select Notify Icon', 'res/', "Icons .png .ico .svg (*.png *.ico *.svg)")[
438 | 0]
439 | if x: self.lineedit_notifyicon.setText(x)
440 |
441 | def func_minimizetotrayclicked(self):
442 | if not self.checkbox_minimizetotray.isChecked():
443 | subprocess.run(['wmctrl', '-i', '-r', str(self.windowid), '-b', 'remove,skip_taskbar'])
444 |
445 | def func_pushbutton_add(self):
446 | self.listWidget.addItem(self.editline_profilepath.text())
447 | self.testforprofile()
448 |
449 | def func_pushbutton_remove(self):
450 | self.listWidget.takeItem(self.listWidget.currentRow())
451 | self.testforprofile()
452 |
453 | def testforprofile(self):
454 | try:
455 | if self.listWidget.count() == 0: raise Exception()
456 | for value in range(self.listWidget.count()):
457 | vv = open(self.listWidget.item(value).text(), 'r')
458 | vv.close()
459 | self.lastmtime = 0
460 | self.label_accountwarrning.hide()
461 | self.badprofile = False
462 | except:
463 | self.label_accountwarrning.setText('ERROR! Please Fix Account List')
464 | self.label_accountwarrning.setStyleSheet('color: red')
465 | self.tabWidget.setCurrentIndex(1)
466 | self.label_accountwarrning.show()
467 | self.badprofile = True
468 |
469 | def timersetup(self):
470 | self.timetriggercheck.timeout.connect(self.fire)
471 | self.timetriggercheck.start(1000)
472 |
473 | def selectfile(self):
474 | x = \
475 | QFileDialog.getOpenFileName(self, 'Select Profile .msf File',
476 | '/home/' + getpass.getuser() + '/.thunderbird/',
477 | "INBOX.msf(INBOX.msf)")[0]
478 | if x: self.editline_profilepath.setText(x)
479 |
480 | def cancel(self):
481 | self.testforprofile()
482 | self.hide()
483 | self.checkBox_fixedwidth.setChecked(self.popup.textBrowser.fixedwidth)
484 | if self.popup.textBrowser.fixedwidth:
485 | self.popup.textBrowser.setLineWrapMode(QtWidgets.QTextBrowser.WidgetWidth)
486 | if not self.popup.textBrowser.fixedwidth:
487 | self.popup.textBrowser.setLineWrapMode(QtWidgets.QTextBrowser.NoWrap)
488 | self.checkBox_favicons.setChecked(self.popup.favicons)
489 | self.label_colour.setStyleSheet('color: ' + self.colour)
490 | self.radioButton_top.setChecked(self.popup.top)
491 | self.radioButton_bottom.setChecked(not self.popup.top)
492 | self.spinBox_xpos.setValue(self.popup.xpos)
493 | self.checkBox_popup.setChecked(self.popup.popupon)
494 | self.lineEdit_notifysound.setText(self.popup.sound.fileName())
495 | self.checkBox_notifysound.setChecked(self.popup.soundon)
496 | self.horizontalSlider_opacity.setValue(int(self.popup.windowOpacity() * 100))
497 | self.spinBox_displaytime.setValue(self.popup.duration)
498 | self.timetriggercheck.start(1000)
499 |
500 | def ok(self):
501 | config = configparser.ConfigParser()
502 | config['popup'] = {}
503 | config['popup']['top'] = str(int(self.radioButton_top.isChecked()))
504 | self.popup.top = self.radioButton_top.isChecked()
505 | config['popup']['fixedwidth'] = str(int(self.checkBox_fixedwidth.isChecked()))
506 | self.popup.textBrowser.fixedwidth = self.checkBox_fixedwidth.isChecked()
507 | if self.popup.textBrowser.fixedwidth: self.popup.textBrowser.setLineWrapMode(QtWidgets.QTextBrowser.WidgetWidth)
508 | if not self.popup.textBrowser.fixedwidth: self.popup.textBrowser.setLineWrapMode(QtWidgets.QTextBrowser.NoWrap)
509 | config['popup']['duration'] = str(self.spinBox_displaytime.value())
510 | self.popup.duration = self.spinBox_displaytime.value()
511 | config['popup']['favicons'] = str(int(self.checkBox_favicons.isChecked()))
512 | self.popup.favicons = self.checkBox_favicons.isChecked()
513 | config['popup']['opacity'] = str(int(self.horizontalSlider_opacity.value()))
514 | self.popup.setWindowOpacity(float(self.horizontalSlider_opacity.value() / 100))
515 | config['popup']['on'] = str(int(self.checkBox_popup.isChecked()))
516 | self.popup.popupon = self.checkBox_popup.isChecked()
517 | config['popup']['soundpath'] = self.lineEdit_notifysound.text()
518 | self.popup.sound = QSound(self.lineEdit_notifysound.text())
519 | config['popup']['soundon'] = str(int(self.checkBox_notifysound.isChecked()))
520 | self.popup.soundon = self.checkBox_notifysound.isChecked()
521 | config['popup']['x'] = str(self.spinBox_xpos.value())
522 | self.popup.xpos = self.spinBox_xpos.value()
523 | config['ticks'] = {}
524 | config['ticks']['minimizetotray'] = str(int(self.checkbox_minimizetotray.isChecked()))
525 | config['ticks']['showcount'] = str(int(self.checkbox_showcount.isChecked()))
526 | config['icons'] = {}
527 | config['icons']['default'] = self.lineedit_defulticon.text()
528 | self.defaulticon = self.lineedit_defulticon.text()
529 | config['icons']['notify'] = self.lineedit_notifyicon.text()
530 | self.notifyicon = self.lineedit_notifyicon.text()
531 | self.colour = self.colour_pre
532 | config['icons']['colour'] = self.colour
533 | config['profiles'] = {}
534 | self.profiles.clear()
535 | for x in range(self.listWidget.count()):
536 | self.profiles.append(self.listWidget.item(x).text())
537 | config['profiles'][str(x)] = self.listWidget.item(x).text()
538 | with open(self.my_settings_file, 'w') as configfile:
539 | config.write(configfile)
540 | configfile.close()
541 | self.hide()
542 | self.tray_icon.setIcon(QtGui.QIcon(self.defaulticon))
543 | self.timetriggercheck.start(1000)
544 | self.lastmtime = 0
545 | self.popup.fire(self.profiles, False)
546 |
547 | def settings(self):
548 | self.show()
549 |
550 | def iconclick(self):
551 | if self.checkbox_minimizetotray.isChecked() and not self.badprofile:
552 | log('self.windowid = ' + str(self.windowid))
553 | log('self.INTRAY = ' + str(self.INTRAY))
554 | self.timetriggercheck.stop()
555 | if checkvisable():
556 | subprocess.run(["xdotool", "windowunmap", self.windowid])
557 | self.INTRAY = True
558 | elif self.winId():
559 | subprocess.run(["xdotool", "windowmap", self.windowid])
560 | subprocess.run(['wmctrl', '-i', '-r', str(self.windowid), '-b', 'remove,skip_taskbar'])
561 | subprocess.run(["xdotool", "windowactivate", self.windowid])
562 | self.INTRAY = False
563 | self.timetriggercheck.start(1000)
564 |
565 | def iconmenushowhide(self):
566 | if not self.badprofile:
567 | self.timetriggercheck.stop()
568 | if checkvisable():
569 | subprocess.run(["xdotool", "windowunmap", self.windowid])
570 | self.INTRAY = True
571 | elif self.winId():
572 | subprocess.run(["xdotool", "windowmap", self.windowid])
573 | subprocess.run(['wmctrl', '-i', '-r', str(self.windowid), '-b', 'remove,skip_taskbar'])
574 | subprocess.run(["xdotool", "windowactivate", self.windowid])
575 | self.INTRAY = False
576 | self.timetriggercheck.start(1000)
577 |
578 | def fire(self):
579 | if self.checkbox_minimizetotray.isChecked() and os.path.isfile('/tmp/tbpassover'):
580 | if self.INTRAY:
581 | self.INTRAY = False
582 | self.popup.textBrowser.INTRAY = False
583 | self.popup_test.textBrowser.INTRAY = False
584 | subprocess.run(['wmctrl', '-i', '-r', str(self.windowid), '-b', 'remove,skip_taskbar'])
585 | elif not self.INTRAY:
586 | subprocess.run(["xdotool", "windowunmap", self.windowid])
587 | self.INTRAY = True
588 | os.remove('/tmp/tbpassover')
589 | log('passover')
590 | stdout = subprocess.run(["pgrep", "-i", "thunderbird"], stdout=subprocess.PIPE).stdout.decode('UTF-8')
591 | if not stdout: sys.exit()
592 | if self.badprofile:
593 | self.label_accountwarrning.setText('ERROR! Please Fix Account List')
594 | self.label_accountwarrning.setStyleSheet('color: red')
595 | self.label_accountwarrning.show()
596 | self.tabWidget.setCurrentIndex(1)
597 | self.tray_icon.showMessage('TBtray Profile Warning', 'Please setup account profiles',
598 | QSystemTrayIcon.Critical)
599 | self.timetriggercheck.start(15000)
600 | return
601 | self.timetriggercheck.stop()
602 | if self.popup.textBrowser.INTRAY or self.popup_test.textBrowser.INTRAY:
603 | self.INTRAY = False
604 | self.popup.textBrowser.INTRAY = False
605 | self.popup_test.textBrowser.INTRAY = False
606 | if not self.windowid:
607 | stdout = subprocess.run(["wmctrl", '-lx'], stdout=subprocess.PIPE).stdout.decode('UTF-8')
608 | idx = re.findall('(\dx\w+)..0 Mail\.thunderbird', str(stdout))
609 | if idx:
610 | self.windowid = idx[0]
611 | self.popup.textBrowser.windowid = self.windowid
612 | log('grabbed window ' + idx[0])
613 | subprocess.run(["xdotool", "windowunmap", self.windowid])
614 | self.INTRAY = True
615 | if self.checkbox_minimizetotray.isChecked() and not self.INTRAY:
616 | if not checkvisable() and self.windowid:
617 | subprocess.run(['wmctrl', '-i', '-r', str(self.windowid), '-b', 'add,skip_taskbar'])
618 | self.INTRAY = True
619 | for profile in self.profiles:
620 | if os.path.getmtime(profile) > self.lastmtime:
621 | self.lastmtime = os.path.getmtime(profile)
622 | self.matches = 0
623 | for profile2 in self.profiles:
624 | if not os.path.isfile(profile2): continue
625 | filetext = subprocess.run(["tail", "-n", "2000", profile2], stdout=subprocess.PIPE).stdout.decode(
626 | 'UTF-8')
627 | matchesx = re.findall('\^A2=(\w+)', filetext)
628 | if matchesx: self.matches += int(matchesx[-1], 16)
629 | if self.matches > 0:
630 | if self.checkbox_showcount.isChecked():
631 | iconpixmap = QtGui.QPixmap(self.notifyicon)
632 | count = str(self.matches)
633 | pixmap = QPixmap(iconpixmap.width(), iconpixmap.height())
634 | fontsize = self.findfontsize(count, pixmap)
635 | font = QFont("Arial", fontsize)
636 | painter = QPainter()
637 | pixmap.fill(Qt.transparent)
638 | painter.begin(pixmap)
639 | painter.setFont(font)
640 | painter.setOpacity(0.3)
641 | painter.drawPixmap(iconpixmap.rect(), iconpixmap)
642 | painter.setOpacity(1.0)
643 | painter.setPen(QColor(self.colour))
644 | fm = QFontMetrics(font)
645 | painter.drawText(int((pixmap.width() - fm.width(count)) / 2),
646 | int((pixmap.height() - fm.height()) / 2 + fm.ascent() + 1), count)
647 | painter.end()
648 | self.tray_icon.setIcon(QtGui.QIcon(pixmap))
649 | else:
650 | self.tray_icon.setIcon(QtGui.QIcon(self.notifyicon))
651 | if not self.badprofile: self.popup.fire(self.profiles)
652 | else:
653 | self.tray_icon.setIcon(QtGui.QIcon(self.defaulticon))
654 | break
655 | self.timetriggercheck.start(1000)
656 |
657 | @staticmethod
658 | def findfontsize(text, pixmap):
659 | x = 4
660 | for x in range(4, 200):
661 | font = QFont("Arial", x)
662 | fm = QFontMetrics(font)
663 | if fm.width(text) > pixmap.width() or fm.height() > pixmap.height():
664 | break
665 | return x
666 |
667 |
668 | def main():
669 | app = QtWidgets.QApplication(sys.argv)
670 | ui = MainApp()
671 | ui.hide()
672 | sys.exit(app.exec_())
673 |
674 |
675 | if __name__ == '__main__':
676 | main()
677 |
--------------------------------------------------------------------------------
/tbtrayui.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | # Form implementation generated from reading ui file '/home/greg/PycharmProjects/tbtray/tbtrayui.ui'
4 | #
5 | # Created by: PyQt5 UI code generator 5.11.3
6 | #
7 | # WARNING! All changes made in this file will be lost!
8 |
9 | from PyQt5 import QtCore, QtGui, QtWidgets
10 |
11 | class Ui_Form(object):
12 | def setupUi(self, Form):
13 | Form.setObjectName("Form")
14 | Form.resize(674, 492)
15 | icon = QtGui.QIcon.fromTheme("thunderbird")
16 | Form.setWindowIcon(icon)
17 | Form.setLayoutDirection(QtCore.Qt.LeftToRight)
18 | self.verticalLayout = QtWidgets.QVBoxLayout(Form)
19 | self.verticalLayout.setObjectName("verticalLayout")
20 | self.tabWidget = QtWidgets.QTabWidget(Form)
21 | self.tabWidget.setObjectName("tabWidget")
22 | self.tab = QtWidgets.QWidget()
23 | self.tab.setObjectName("tab")
24 | self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.tab)
25 | self.verticalLayout_3.setObjectName("verticalLayout_3")
26 | self.groupBox_6 = QtWidgets.QGroupBox(self.tab)
27 | self.groupBox_6.setMinimumSize(QtCore.QSize(0, 160))
28 | self.groupBox_6.setMaximumSize(QtCore.QSize(16777215, 160))
29 | self.groupBox_6.setObjectName("groupBox_6")
30 | self.verticalLayout_5 = QtWidgets.QVBoxLayout(self.groupBox_6)
31 | self.verticalLayout_5.setObjectName("verticalLayout_5")
32 | self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
33 | self.horizontalLayout_4.setObjectName("horizontalLayout_4")
34 | self.label_5 = QtWidgets.QLabel(self.groupBox_6)
35 | self.label_5.setMinimumSize(QtCore.QSize(0, 36))
36 | self.label_5.setObjectName("label_5")
37 | self.horizontalLayout_4.addWidget(self.label_5)
38 | self.lineedit_defulticon = QtWidgets.QLineEdit(self.groupBox_6)
39 | self.lineedit_defulticon.setMinimumSize(QtCore.QSize(400, 0))
40 | self.lineedit_defulticon.setObjectName("lineedit_defulticon")
41 | self.horizontalLayout_4.addWidget(self.lineedit_defulticon)
42 | self.toolButton_defaulticon = QtWidgets.QToolButton(self.groupBox_6)
43 | self.toolButton_defaulticon.setObjectName("toolButton_defaulticon")
44 | self.horizontalLayout_4.addWidget(self.toolButton_defaulticon)
45 | self.verticalLayout_5.addLayout(self.horizontalLayout_4)
46 | self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
47 | self.horizontalLayout_5.setObjectName("horizontalLayout_5")
48 | self.label_6 = QtWidgets.QLabel(self.groupBox_6)
49 | self.label_6.setMinimumSize(QtCore.QSize(0, 36))
50 | self.label_6.setObjectName("label_6")
51 | self.horizontalLayout_5.addWidget(self.label_6)
52 | self.lineedit_notifyicon = QtWidgets.QLineEdit(self.groupBox_6)
53 | self.lineedit_notifyicon.setMinimumSize(QtCore.QSize(400, 0))
54 | self.lineedit_notifyicon.setObjectName("lineedit_notifyicon")
55 | self.horizontalLayout_5.addWidget(self.lineedit_notifyicon)
56 | self.toolButton_notifyicon = QtWidgets.QToolButton(self.groupBox_6)
57 | self.toolButton_notifyicon.setObjectName("toolButton_notifyicon")
58 | self.horizontalLayout_5.addWidget(self.toolButton_notifyicon)
59 | self.verticalLayout_5.addLayout(self.horizontalLayout_5)
60 | self.horizontalLayout_6 = QtWidgets.QHBoxLayout()
61 | self.horizontalLayout_6.setObjectName("horizontalLayout_6")
62 | self.checkbox_minimizetotray = QtWidgets.QCheckBox(self.groupBox_6)
63 | self.checkbox_minimizetotray.setMinimumSize(QtCore.QSize(0, 0))
64 | self.checkbox_minimizetotray.setMaximumSize(QtCore.QSize(16777215, 16777215))
65 | self.checkbox_minimizetotray.setObjectName("checkbox_minimizetotray")
66 | self.horizontalLayout_6.addWidget(self.checkbox_minimizetotray)
67 | self.checkbox_showcount = QtWidgets.QCheckBox(self.groupBox_6)
68 | self.checkbox_showcount.setObjectName("checkbox_showcount")
69 | self.horizontalLayout_6.addWidget(self.checkbox_showcount)
70 | self.pushButton_colourpicker = QtWidgets.QPushButton(self.groupBox_6)
71 | self.pushButton_colourpicker.setObjectName("pushButton_colourpicker")
72 | self.horizontalLayout_6.addWidget(self.pushButton_colourpicker)
73 | self.label_colour = QtWidgets.QLabel(self.groupBox_6)
74 | self.label_colour.setObjectName("label_colour")
75 | self.horizontalLayout_6.addWidget(self.label_colour)
76 | spacerItem = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
77 | self.horizontalLayout_6.addItem(spacerItem)
78 | self.verticalLayout_5.addLayout(self.horizontalLayout_6)
79 | self.verticalLayout_3.addWidget(self.groupBox_6)
80 | self.groupBox_4 = QtWidgets.QGroupBox(self.tab)
81 | self.groupBox_4.setMinimumSize(QtCore.QSize(0, 60))
82 | self.groupBox_4.setMaximumSize(QtCore.QSize(16777215, 60))
83 | self.groupBox_4.setObjectName("groupBox_4")
84 | self.horizontalLayout_8 = QtWidgets.QHBoxLayout(self.groupBox_4)
85 | self.horizontalLayout_8.setObjectName("horizontalLayout_8")
86 | self.checkBox_notifysound = QtWidgets.QCheckBox(self.groupBox_4)
87 | self.checkBox_notifysound.setObjectName("checkBox_notifysound")
88 | self.horizontalLayout_8.addWidget(self.checkBox_notifysound)
89 | self.lineEdit_notifysound = QtWidgets.QLineEdit(self.groupBox_4)
90 | self.lineEdit_notifysound.setObjectName("lineEdit_notifysound")
91 | self.horizontalLayout_8.addWidget(self.lineEdit_notifysound)
92 | self.toolButton_notifysound = QtWidgets.QToolButton(self.groupBox_4)
93 | self.toolButton_notifysound.setObjectName("toolButton_notifysound")
94 | self.horizontalLayout_8.addWidget(self.toolButton_notifysound)
95 | self.verticalLayout_3.addWidget(self.groupBox_4)
96 | self.groupBox_5 = QtWidgets.QGroupBox(self.tab)
97 | self.groupBox_5.setMinimumSize(QtCore.QSize(0, 150))
98 | self.groupBox_5.setMaximumSize(QtCore.QSize(16777215, 150))
99 | self.groupBox_5.setFlat(False)
100 | self.groupBox_5.setObjectName("groupBox_5")
101 | self.verticalLayout_4 = QtWidgets.QVBoxLayout(self.groupBox_5)
102 | self.verticalLayout_4.setObjectName("verticalLayout_4")
103 | self.horizontalLayout_13 = QtWidgets.QHBoxLayout()
104 | self.horizontalLayout_13.setObjectName("horizontalLayout_13")
105 | self.checkBox_popup = QtWidgets.QCheckBox(self.groupBox_5)
106 | self.checkBox_popup.setMinimumSize(QtCore.QSize(0, 0))
107 | self.checkBox_popup.setMaximumSize(QtCore.QSize(120, 16777215))
108 | self.checkBox_popup.setObjectName("checkBox_popup")
109 | self.horizontalLayout_13.addWidget(self.checkBox_popup)
110 | self.checkBox_fixedwidth = QtWidgets.QCheckBox(self.groupBox_5)
111 | self.checkBox_fixedwidth.setObjectName("checkBox_fixedwidth")
112 | self.horizontalLayout_13.addWidget(self.checkBox_fixedwidth)
113 | self.label_4 = QtWidgets.QLabel(self.groupBox_5)
114 | self.label_4.setObjectName("label_4")
115 | self.horizontalLayout_13.addWidget(self.label_4)
116 | self.spinBox_displaytime = QtWidgets.QSpinBox(self.groupBox_5)
117 | self.spinBox_displaytime.setMinimum(1)
118 | self.spinBox_displaytime.setProperty("value", 10)
119 | self.spinBox_displaytime.setObjectName("spinBox_displaytime")
120 | self.horizontalLayout_13.addWidget(self.spinBox_displaytime)
121 | self.label_3 = QtWidgets.QLabel(self.groupBox_5)
122 | self.label_3.setObjectName("label_3")
123 | self.horizontalLayout_13.addWidget(self.label_3)
124 | self.spinBox_xpos = QtWidgets.QSpinBox(self.groupBox_5)
125 | self.spinBox_xpos.setMinimumSize(QtCore.QSize(99, 0))
126 | self.spinBox_xpos.setMaximum(10000)
127 | self.spinBox_xpos.setProperty("value", 1585)
128 | self.spinBox_xpos.setObjectName("spinBox_xpos")
129 | self.horizontalLayout_13.addWidget(self.spinBox_xpos)
130 | spacerItem1 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
131 | self.horizontalLayout_13.addItem(spacerItem1)
132 | self.verticalLayout_4.addLayout(self.horizontalLayout_13)
133 | self.horizontalLayout_12 = QtWidgets.QHBoxLayout()
134 | self.horizontalLayout_12.setObjectName("horizontalLayout_12")
135 | self.radioButton_top = QtWidgets.QRadioButton(self.groupBox_5)
136 | font = QtGui.QFont()
137 | font.setKerning(True)
138 | self.radioButton_top.setFont(font)
139 | self.radioButton_top.setObjectName("radioButton_top")
140 | self.horizontalLayout_12.addWidget(self.radioButton_top)
141 | self.radioButton_bottom = QtWidgets.QRadioButton(self.groupBox_5)
142 | self.radioButton_bottom.setObjectName("radioButton_bottom")
143 | self.horizontalLayout_12.addWidget(self.radioButton_bottom)
144 | self.checkBox_favicons = QtWidgets.QCheckBox(self.groupBox_5)
145 | self.checkBox_favicons.setMinimumSize(QtCore.QSize(0, 0))
146 | self.checkBox_favicons.setObjectName("checkBox_favicons")
147 | self.horizontalLayout_12.addWidget(self.checkBox_favicons)
148 | self.label = QtWidgets.QLabel(self.groupBox_5)
149 | self.label.setObjectName("label")
150 | self.horizontalLayout_12.addWidget(self.label)
151 | self.horizontalSlider_opacity = QtWidgets.QSlider(self.groupBox_5)
152 | self.horizontalSlider_opacity.setMinimumSize(QtCore.QSize(210, 0))
153 | self.horizontalSlider_opacity.setMaximum(100)
154 | self.horizontalSlider_opacity.setOrientation(QtCore.Qt.Horizontal)
155 | self.horizontalSlider_opacity.setTickPosition(QtWidgets.QSlider.NoTicks)
156 | self.horizontalSlider_opacity.setObjectName("horizontalSlider_opacity")
157 | self.horizontalLayout_12.addWidget(self.horizontalSlider_opacity)
158 | spacerItem2 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
159 | self.horizontalLayout_12.addItem(spacerItem2)
160 | self.verticalLayout_4.addLayout(self.horizontalLayout_12)
161 | self.toolButton_firepopup = QtWidgets.QPushButton(self.groupBox_5)
162 | self.toolButton_firepopup.setMinimumSize(QtCore.QSize(0, 0))
163 | self.toolButton_firepopup.setMaximumSize(QtCore.QSize(84, 16777215))
164 | self.toolButton_firepopup.setObjectName("toolButton_firepopup")
165 | self.verticalLayout_4.addWidget(self.toolButton_firepopup)
166 | self.verticalLayout_3.addWidget(self.groupBox_5)
167 | spacerItem3 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Expanding)
168 | self.verticalLayout_3.addItem(spacerItem3)
169 | self.tabWidget.addTab(self.tab, "")
170 | self.tab_2 = QtWidgets.QWidget()
171 | self.tab_2.setObjectName("tab_2")
172 | self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.tab_2)
173 | self.verticalLayout_2.setObjectName("verticalLayout_2")
174 | self.groupBox = QtWidgets.QGroupBox(self.tab_2)
175 | self.groupBox.setObjectName("groupBox")
176 | self.horizontalLayout = QtWidgets.QHBoxLayout(self.groupBox)
177 | self.horizontalLayout.setObjectName("horizontalLayout")
178 | self.editline_profilepath = QtWidgets.QLineEdit(self.groupBox)
179 | self.editline_profilepath.setObjectName("editline_profilepath")
180 | self.horizontalLayout.addWidget(self.editline_profilepath)
181 | self.toolButton_profilepath = QtWidgets.QToolButton(self.groupBox)
182 | self.toolButton_profilepath.setObjectName("toolButton_profilepath")
183 | self.horizontalLayout.addWidget(self.toolButton_profilepath)
184 | self.verticalLayout_2.addWidget(self.groupBox)
185 | self.frame_2 = QtWidgets.QFrame(self.tab_2)
186 | self.frame_2.setFrameShape(QtWidgets.QFrame.StyledPanel)
187 | self.frame_2.setFrameShadow(QtWidgets.QFrame.Raised)
188 | self.frame_2.setObjectName("frame_2")
189 | self.horizontalLayout_3 = QtWidgets.QHBoxLayout(self.frame_2)
190 | self.horizontalLayout_3.setObjectName("horizontalLayout_3")
191 | self.label_2 = QtWidgets.QLabel(self.frame_2)
192 | font = QtGui.QFont()
193 | font.setPointSize(10)
194 | font.setBold(True)
195 | font.setWeight(75)
196 | self.label_2.setFont(font)
197 | self.label_2.setObjectName("label_2")
198 | self.horizontalLayout_3.addWidget(self.label_2)
199 | spacerItem4 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
200 | self.horizontalLayout_3.addItem(spacerItem4)
201 | self.label_accountwarrning = QtWidgets.QLabel(self.frame_2)
202 | font = QtGui.QFont()
203 | font.setPointSize(20)
204 | self.label_accountwarrning.setFont(font)
205 | self.label_accountwarrning.setText("")
206 | self.label_accountwarrning.setScaledContents(True)
207 | self.label_accountwarrning.setObjectName("label_accountwarrning")
208 | self.horizontalLayout_3.addWidget(self.label_accountwarrning)
209 | spacerItem5 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
210 | self.horizontalLayout_3.addItem(spacerItem5)
211 | self.pushButton_add = QtWidgets.QPushButton(self.frame_2)
212 | self.pushButton_add.setObjectName("pushButton_add")
213 | self.horizontalLayout_3.addWidget(self.pushButton_add)
214 | self.pushButton_remove = QtWidgets.QPushButton(self.frame_2)
215 | self.pushButton_remove.setObjectName("pushButton_remove")
216 | self.horizontalLayout_3.addWidget(self.pushButton_remove)
217 | self.verticalLayout_2.addWidget(self.frame_2)
218 | self.listWidget = QtWidgets.QListWidget(self.tab_2)
219 | self.listWidget.setObjectName("listWidget")
220 | self.verticalLayout_2.addWidget(self.listWidget)
221 | self.tabWidget.addTab(self.tab_2, "")
222 | self.verticalLayout.addWidget(self.tabWidget)
223 | self.frame = QtWidgets.QFrame(Form)
224 | self.frame.setFrameShape(QtWidgets.QFrame.StyledPanel)
225 | self.frame.setFrameShadow(QtWidgets.QFrame.Raised)
226 | self.frame.setObjectName("frame")
227 | self.horizontalLayout_2 = QtWidgets.QHBoxLayout(self.frame)
228 | self.horizontalLayout_2.setObjectName("horizontalLayout_2")
229 | spacerItem6 = QtWidgets.QSpacerItem(40, 20, QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Minimum)
230 | self.horizontalLayout_2.addItem(spacerItem6)
231 | self.pushButton_cancel = QtWidgets.QPushButton(self.frame)
232 | self.pushButton_cancel.setObjectName("pushButton_cancel")
233 | self.horizontalLayout_2.addWidget(self.pushButton_cancel)
234 | self.pushButton_ok = QtWidgets.QPushButton(self.frame)
235 | self.pushButton_ok.setObjectName("pushButton_ok")
236 | self.horizontalLayout_2.addWidget(self.pushButton_ok)
237 | self.verticalLayout.addWidget(self.frame)
238 |
239 | self.retranslateUi(Form)
240 | self.tabWidget.setCurrentIndex(0)
241 | QtCore.QMetaObject.connectSlotsByName(Form)
242 |
243 | def retranslateUi(self, Form):
244 | _translate = QtCore.QCoreApplication.translate
245 | Form.setWindowTitle(_translate("Form", "TBTray Settings"))
246 | self.groupBox_6.setTitle(_translate("Form", "Tray Icon"))
247 | self.label_5.setText(_translate("Form", "Default Icon Path"))
248 | self.toolButton_defaulticon.setText(_translate("Form", "..."))
249 | self.label_6.setText(_translate("Form", "Notify Icon Path"))
250 | self.toolButton_notifyicon.setText(_translate("Form", "..."))
251 | self.checkbox_minimizetotray.setText(_translate("Form", "Minimize to tray"))
252 | self.checkbox_showcount.setText(_translate("Form", "Show unread Count"))
253 | self.pushButton_colourpicker.setText(_translate("Form", "Colour Picker"))
254 | self.label_colour.setText(_translate("Form", "Colour of unread count font"))
255 | self.groupBox_4.setTitle(_translate("Form", "Notify Sound"))
256 | self.checkBox_notifysound.setText(_translate("Form", "On/Off"))
257 | self.toolButton_notifysound.setText(_translate("Form", "..."))
258 | self.groupBox_5.setTitle(_translate("Form", "Popup"))
259 | self.checkBox_popup.setText(_translate("Form", "ON/OFF"))
260 | self.checkBox_fixedwidth.setToolTip(_translate("Form", "Fixed or variable width popup"))
261 | self.checkBox_fixedwidth.setText(_translate("Form", "Fixed Width"))
262 | self.label_4.setText(_translate("Form", " Display Time"))
263 | self.spinBox_displaytime.setSuffix(_translate("Form", " sec\'s"))
264 | self.label_3.setText(_translate("Form", "Popup X position"))
265 | self.spinBox_xpos.setSuffix(_translate("Form", " px"))
266 | self.radioButton_top.setText(_translate("Form", "&Top-Right"))
267 | self.radioButton_bottom.setText(_translate("Form", "Bottom-Right"))
268 | self.checkBox_favicons.setText(_translate("Form", "Show Mail Favicons"))
269 | self.label.setText(_translate("Form", " Opacity"))
270 | self.toolButton_firepopup.setText(_translate("Form", "Popup Test"))
271 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("Form", "General"))
272 | self.groupBox.setTitle(_translate("Form", "Profile Path selector"))
273 | self.toolButton_profilepath.setText(_translate("Form", "..."))
274 | self.label_2.setText(_translate("Form", "Profile List ( .msf files)"))
275 | self.pushButton_add.setText(_translate("Form", "Add"))
276 | self.pushButton_remove.setText(_translate("Form", "Remove"))
277 | self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("Form", "Accounts"))
278 | self.pushButton_cancel.setText(_translate("Form", "Cancel"))
279 | self.pushButton_ok.setText(_translate("Form", "OK"))
280 |
281 |
--------------------------------------------------------------------------------
/tbtrayui.ui:
--------------------------------------------------------------------------------
1 |
2 |