#!/usr/bin/python3 import gi import config import logging import schedule import sqlite3 from threading import Thread from datetime import datetime from time import sleep from random import choice from string import ascii_lowercase, digits from playsound import playsound import pyttsx3 gi.require_version("Gtk", "3.0") from gi.repository import Gtk import db ttsEngine = pyttsx3.init('espeak') ttsEngine.setProperty('voice', 'female3') ttsEngine.setProperty('speed', 40) log = logging.getLogger("main") def play_transmissions(timeslot): schedulerEngine = pyttsx3.init('espeak') schedulerEngine.setProperty('voice', 'female3') schedulerEngine.setProperty('speed', 40) log = logging.getLogger("main") try: schedulerConnection = sqlite3.connect(db.database) schedulerCursor = schedulerConnection.cursor() schedulerCursor.execute('select sqlite_version();') except sqlite3.Error as error: log.critical('Could not connect scheduler to sqlite. An error occured: ', error) exit(1) date = datetime.now().date().isoformat() toPlay = db.get_transmissions_by_timeslot(date, timeslot, schedulerCursor) for transmission in toPlay: message = transmission["message"] author = transmission["author"] title = transmission["title"] text = "Author: %s, Title: %s. Message: %s" % ( author, title, message) schedulerEngine.say(text) schedulerEngine.runAndWait() schedulerConnection.close() for i in range(len(config.timeslots)): schedule.every().day.at(config.timeslots[i]).do(play_transmissions, i) def scheduler_loop(): while True: schedule.run_pending() sleep(30) scheduler_thread = Thread(target=scheduler_loop) scheduler_thread.daemon = True scheduler_thread.start() db.create_transmissions_table() class MainWindow(Gtk.Window): def __init__(self): super().__init__(title="bardzo biedny BBS %s"%config.version) ############################## Boxes ##################################### pad=15 self.main_box = Gtk.Box() self.main_box.set_orientation(Gtk.Orientation.HORIZONTAL) # left side self.left_box = Gtk.Box() self.left_box.set_orientation(Gtk.Orientation.VERTICAL) self.left_box.set_spacing(0) self.left_button_box = Gtk.Box() self.left_button_box.set_orientation(Gtk.Orientation.HORIZONTAL) self.left_button_box.set_spacing(pad) # right side self.right_box = Gtk.Box() self.right_box.set_orientation(Gtk.Orientation.VERTICAL) self.right_box.set_spacing(0) self.title_author_box = Gtk.Box() self.title_author_box.set_orientation(Gtk.Orientation.HORIZONTAL) self.title_author_box.set_spacing(pad) self.message_box = Gtk.Box() self.message_box.set_orientation(Gtk.Orientation.VERTICAL) self.message_box.set_spacing(2) self.date_time_box = Gtk.Box() self.date_time_box.set_orientation(Gtk.Orientation.HORIZONTAL) self.date_time_box.set_spacing(pad) self.timeslot_enable_box = Gtk.Box() self.timeslot_enable_box.set_orientation(Gtk.Orientation.VERTICAL) self.timeslot_enable_box.set_spacing(pad) self.play_box = Gtk.Box() self.play_box.set_orientation(Gtk.Orientation.HORIZONTAL) self.play_box.set_spacing(pad) self.clear_play_box = Gtk.Box() self.clear_play_box.set_orientation(Gtk.Orientation.HORIZONTAL) self.clear_play_box.set_spacing(pad) self.clear_save_box = Gtk.Box() self.clear_save_box.set_orientation(Gtk.Orientation.HORIZONTAL) self.clear_save_box.set_spacing(pad) ################################ Widgets ###################################### # left side self.transmission_list = Gtk.ListBox() self.transmission_list.connect("row-selected", self.on_picked_transmission) self.transmissions = [] self.update_transmission_list() self.add_transmission_button = Gtk.Button(label="Add transmission") self.add_transmission_button.connect("clicked", self.on_add_transmission) self.delete_transmission_button = Gtk.Button(label="Delete transmission") self.delete_transmission_button.connect("clicked", self.on_delete_transmission) # right side self.title = Gtk.Entry() self.title.set_placeholder_text("Title") self.title.connect("changed", self.update_save_button) self.author = Gtk.Entry() self.author.set_placeholder_text("Author") self.author.connect("changed", self.update_save_button) self.message_label = Gtk.Label(label="Message") self.message_scroll_window = Gtk.ScrolledWindow() self.message = Gtk.TextView() self.message.get_buffer().connect("changed", self.update_save_button) self.message.set_wrap_mode(Gtk.WrapMode.WORD) self.calendar = Gtk.Calendar() self.calendar.connect("day-selected", self.on_date_picked) year, month, day = self.calendar.get_date() month += 1 month = str(month).rjust(2, '0') day = str(day).rjust(2, '0') self.picked_date = "%i-%s-%s"%(year, month, day) self.today = self.picked_date self.timeslots = Gtk.ListBox() self.timeslot_checkbuttons = [] for i in range(len(config.timeslots)): slot = config.timeslots[i] self.timeslot_checkbuttons.append(Gtk.CheckButton()) self.timeslot_checkbuttons[i].connect("toggled", self.update_save_button) row = Gtk.ListBoxRow() hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=50) row.add(hbox) time_label = Gtk.Label(label=slot, xalign=0) hbox.pack_start(time_label, True, True, 0) hbox.pack_start(self.timeslot_checkbuttons[i], False, True, 0) self.timeslots.add(row) self.play_label = Gtk.Label(label="enable this message?") self.play_switch = Gtk.Switch() self.play_button = Gtk.Button(label="play") self.play_button.connect("clicked", self.on_play_clicked) self.clear_button = Gtk.Button(label="clear") self.clear_button.connect("clicked", self.clear_transmission) self.save_button = Gtk.Button(label="save") self.save_button.set_sensitive(False) self.save_button.connect("clicked", self.on_transmission_saved) # we need to put the buttons in a sizegroup to make the synthesize button the same length as the rest self.right_sizegroup = Gtk.SizeGroup(mode=Gtk.SizeGroupMode.HORIZONTAL) self.right_sizegroup.add_widget(self.play_button) self.right_sizegroup.add_widget(self.clear_button) self.right_sizegroup.add_widget(self.save_button) ######################### organise objects ################################### self.add(self.main_box) self.main_box.pack_start(self.left_box, True, True, pad) self.main_box.pack_start(self.right_box, True, True, pad) self.left_box.pack_start(self.transmission_list, True, True, pad) self.left_box.pack_start(self.left_button_box, False, True, pad) self.left_button_box.pack_start(self.add_transmission_button, True, True, 0) self.left_button_box.pack_start(self.delete_transmission_button, True, True, 0) self.right_box.pack_start(self.title_author_box, False, True, pad) self.title_author_box.pack_start(self.title, True, True, 0) self.title_author_box.pack_start(self.author, True, True, 0) self.right_box.pack_start(self.message_box, True, True, pad) self.message_box.pack_start(self.message_label, False, True, 0) self.message_box.pack_start(self.message_scroll_window, True, True, 0) self.message_scroll_window.add(self.message) self.right_box.pack_start(self.date_time_box, True, True, pad) self.date_time_box.pack_start(self.calendar, True, True, 0) self.date_time_box.pack_start(self.timeslot_enable_box, True, True, 0) self.timeslot_enable_box.pack_start(self.timeslots, True, True, 0) self.timeslot_enable_box.pack_start(self.play_box, False, True, 0) self.play_box.pack_start(self.play_label, True, True, 0) self.play_box.pack_start(self.play_switch, False, False, 0) self.right_box.pack_start(self.clear_play_box, False, True, 0) self.clear_play_box.pack_start(self.clear_button, True, True, 0) self.clear_play_box.pack_start(self.play_button, True, True, 0) self.right_box.pack_start(self.clear_save_box, False, True, pad) self.clear_save_box.pack_start(self.save_button, True, True, 0) ############################## Helper functions ############################# # The save button should be greyed out until all the data is input def update_save_button(self, user_data): messageBuffer = self.message.get_buffer() startIter, endIter = messageBuffer.get_bounds() message = messageBuffer.get_text(startIter, endIter, False) if self.author.get_text() != "" and self.title.get_text() != "" and message != "" and self.get_selected_timeslots() != "": self.save_button.set_sensitive(True) else: self.save_button.set_sensitive(False) def on_date_picked(self, user_data): year, month, day = self.calendar.get_date() month += 1 month = str(month).rjust(2, '0') day = str(day).rjust(2, '0') self.picked_date = "%i-%s-%s"%(year, month, day) def get_selected_timeslots(self): selected = [] for i in range(len(config.timeslots)): if self.timeslot_checkbuttons[i].get_active(): selected.append(str(i)) return str(selected) def update_transmission_list(self): self.transmissions = db.get_transmissions() for child in self.transmission_list.get_children(): self.transmission_list.remove(child) for i in range(len(self.transmissions)): trx = self.transmissions[i] trxID = "%s by %s at %s" % (trx[3], trx[2], trx[1]) # TODO: replace with dictionary for readability label = Gtk.Label(label=trxID) if not trx[-1]: # if the transmission is not to be played label.set_markup('%s'%trxID) self.transmission_list.insert(label, -1) self.transmission_list.show_all() def clear_transmission(self, user_data=None): self.author.set_text("") self.title.set_text("") self.message.get_buffer().set_text("") year, month, day = self.today.split("-") self.calendar.select_day(int(day)) self.calendar.select_month(int(month)-1, int(year)) for i in range(len(self.timeslots)): self.timeslot_checkbuttons[int(i)].set_active(False) self.play_switch.set_state(False) ############################ Button functions ############################### def on_add_transmission(self, user_data): uid = "".join(choice(ascii_lowercase + digits) for char in range(8)) log.info("creating transmission %s" % uid) log.debug(uid) db.add_transmission(uid) self.update_transmission_list() self.transmission_list.select_row(self.transmission_list.get_row_at_index(len(self.transmissions)-1)) def on_delete_transmission(self, user_data): uid = self.transmissions[self.transmission_list.get_selected_row().get_index()][0] log.info("deleting transmission %s" % uid) db.delete_transmission(uid) self.update_transmission_list() self.transmission_list.select_row(self.transmission_list.get_row_at_index(len(self.transmissions)-1)) def on_picked_transmission(self, row, user_data): try: log.debug("row %i activated" % self.transmission_list.get_selected_row().get_index()) uid = self.transmissions[self.transmission_list.get_selected_row().get_index()][0] trxData = db.get_transmission_data(uid) except AttributeError: # when adding a new transmission, this function is called before any row is selected and logs # an error. I don't know when or why that happens, but this does the job. return except: log.critical(error) exit(1) try: self.clear_transmission() self.author.set_text(trxData["author"]) self.title.set_text(trxData["title"]) self.message.get_buffer().set_text(trxData["message"]) year, month, day = trxData["date"].split("-") self.calendar.select_day(int(day)) self.calendar.select_month(int(month)-1, int(year)) timeSlots = eval(trxData["timeslots"]) for i in timeSlots: self.timeslot_checkbuttons[int(i)].set_active(True) self.play_switch.set_state(trxData["play"]) except TypeError: self.clear_transmission() def on_play_clicked(self, user_data): messageBuffer = self.message.get_buffer() startIter, endIter = messageBuffer.get_bounds() message = messageBuffer.get_text(startIter, endIter, False) text = "Author: %s, Title: %s. Message: %s" % ( self.author.get_buffer().get_text(),\ self.title.get_buffer().get_text(),\ message) ttsEngine.say(text) ttsEngine.runAndWait() def on_transmission_saved(self, user_data): uid = self.transmissions[self.transmission_list.get_selected_row().get_index()][0] log.info("saving transmission %s" % uid) messageBuffer = self.message.get_buffer() startIter, endIter = messageBuffer.get_bounds() message = messageBuffer.get_text(startIter, endIter, False) db.edit_transmission(uid, self.picked_date,\ self.author.get_buffer().get_text(),\ self.title.get_buffer().get_text(),\ message, self.get_selected_timeslots(),\ 1,\ self.play_switch.get_state()) self.update_transmission_list() for i in range(len(self.transmissions)): if self.transmissions[i][0] == uid: self.transmission_list.select_row(self.transmission_list.get_row_at_index(i)) win = MainWindow() win.connect("destroy", Gtk.main_quit) win.show_all() Gtk.main() db.sqliteConnection.close() log.debug("database connection closed")