class TestProjectorDB(TestCase): """ Test case for ProjectorDB """ @patch('openlp.core.lib.projector.db.init_url') def setUp(self, mocked_init_url): """ Set up anything necessary for all tests """ mocked_init_url.return_value = 'sqlite:///{db}'.format(db=TEST_DB) self.projector = ProjectorDB() def tearDown(self): """ Clean up """ self.projector.session.close() self.projector = None retries = 0 while retries < 5: try: if os.path.exists(TEST_DB): os.unlink(TEST_DB) break except: time.sleep(1) retries += 1 def test_find_record_by_ip(self): """ Test find record by IP """ # GIVEN: Record entries in database add_records(self.projector, [Projector(**TEST1_DATA), Projector(**TEST2_DATA)]) # WHEN: Search for record using IP record = self.projector.get_projector_by_ip(TEST2_DATA['ip']) # THEN: Verify proper record returned self.assertTrue(compare_data(Projector(**TEST2_DATA), record), 'Record found should have been test_2 data') def test_find_record_by_name(self): """ Test find record by name """ # GIVEN: Record entries in database add_records(self.projector, [Projector(**TEST1_DATA), Projector(**TEST2_DATA)]) # WHEN: Search for record using name record = self.projector.get_projector_by_name(TEST2_DATA['name']) # THEN: Verify proper record returned self.assertTrue(compare_data(Projector(**TEST2_DATA), record), 'Record found should have been test_2 data') def test_record_delete(self): """ Test record can be deleted """ # GIVEN: Record in database add_records(self.projector, [ Projector(**TEST3_DATA), ]) record = self.projector.get_projector_by_ip(TEST3_DATA['ip']) # WHEN: Record deleted self.projector.delete_projector(record) # THEN: Verify record not retrievable found = self.projector.get_projector_by_ip(TEST3_DATA['ip']) self.assertFalse(found, 'test_3 record should have been deleted') def test_record_edit(self): """ Test edited record returns the same record ID with different data """ # GIVEN: Record entries in database add_records(self.projector, [Projector(**TEST1_DATA), Projector(**TEST2_DATA)]) # WHEN: We retrieve a specific record record = self.projector.get_projector_by_ip(TEST1_DATA['ip']) record_id = record.id # WHEN: Data is changed record.ip = TEST3_DATA['ip'] record.port = TEST3_DATA['port'] record.pin = TEST3_DATA['pin'] record.name = TEST3_DATA['name'] record.location = TEST3_DATA['location'] record.notes = TEST3_DATA['notes'] updated = self.projector.update_projector(record) self.assertTrue(updated, 'Save updated record should have returned True') record = self.projector.get_projector_by_ip(TEST3_DATA['ip']) # THEN: Record ID should remain the same, but data should be changed self.assertEqual(record_id, record.id, 'Edited record should have the same ID') self.assertTrue(compare_data(Projector(**TEST3_DATA), record), 'Edited record should have new data') def test_source_add(self): """ Test source entry for projector item """ # GIVEN: Record entries in database projector1 = Projector(**TEST1_DATA) self.projector.add_projector(projector1) item = self.projector.get_projector_by_id(projector1.id) item_id = item.id # WHEN: A source entry is saved for item source = ProjectorSource(projector_id=item_id, code='11', text='First RGB source') self.projector.add_source(source) # THEN: Projector should have the same source entry item = self.projector.get_projector_by_id(item_id) self.assertTrue(compare_source(item.source_list[0], source)) def test_manufacturer_repr(self): """ Test Manufacturer.__repr__ text """ # GIVEN: Test object manufacturer = Manufacturer() # WHEN: Name is set manufacturer.name = 'OpenLP Test' # THEN: __repr__ should return a proper string self.assertEqual( str(manufacturer), '<Manufacturer(name="OpenLP Test")>', 'Manufacturer.__repr__() should have returned a proper representation string' ) def test_model_repr(self): """ Test Model.__repr__ text """ # GIVEN: Test object model = Model() # WHEN: Name is set model.name = 'OpenLP Test' # THEN: __repr__ should return a proper string self.assertEqual( str(model), '<Model(name=' "OpenLP Test" ')>', 'Model.__repr__() should have returned a proper representation string' ) def test_source_repr(self): """ Test Source.__repr__ text """ # GIVEN: Test object source = Source() # WHEN: Source() information is set source.pjlink_name = 'Test object' source.pjlink_code = '11' source.text = 'Input text' # THEN: __repr__ should return a proper string self.assertEqual( str(source), '<Source(pjlink_name="Test object", pjlink_code="11", text="Input text")>', 'Source.__repr__() should have returned a proper representation string' ) def test_projector_repr(self): """ Test Projector.__repr__() text """ # GIVEN: Test object projector = Projector() # WHEN: projector() is populated # NOTE: projector.pin, projector.other, projector.sources should all return None # projector.source_list should return an empty list projector.id = 0 projector.ip = '127.0.0.1' projector.port = PJLINK_PORT projector.name = 'Test One' projector.location = 'Somewhere over the rainbow' projector.notes = 'Not again' projector.pjlink_name = 'TEST' projector.manufacturer = 'IN YOUR DREAMS' projector.model = 'OpenLP' # THEN: __repr__ should return a proper string self.assertEqual( str(projector), '< Projector(id="0", ip="127.0.0.1", port="4352", pin="None", name="Test One", ' 'location="Somewhere over the rainbow", notes="Not again", pjlink_name="TEST", ' 'manufacturer="IN YOUR DREAMS", model="OpenLP", other="None", sources="None", ' 'source_list="[]") >', 'Projector.__repr__() should have returned a proper representation string' ) def test_projectorsource_repr(self): """ Test ProjectorSource.__repr__() text """ # GIVEN: test setup projector1 = Projector(**TEST1_DATA) self.projector.add_projector(projector1) item = self.projector.get_projector_by_id(projector1.id) item_id = item.id # WHEN: A source entry is saved for item source = ProjectorSource(projector_id=item_id, code='11', text='First RGB source') self.projector.add_source(source) # THEN: __repr__ should return a proper string self.assertEqual( str(source), '<ProjectorSource(id="1", code="11", text="First RGB source", projector_id="1")>', 'ProjectorSource.__repr__)_ should have returned a proper representation string' ) def test_get_projector_by_id_none(self): """ Test get_projector_by_id returns None if no db entry """ # GIVEN: Test object and data projector = self.projector # WHEN: DB search for entry not saved results = projector.get_projector_by_id(dbid=123134556409824506) # THEN: Verify return was None self.assertEqual(results, None, 'Returned results should have equaled None')
class TestProjectorDB(TestCase): """ Test case for ProjectorDB """ def setUp(self): """ Set up anything necessary for all tests """ if not hasattr(self, "projector"): with patch("openlp.core.lib.projector.db.init_url") as mocked_init_url: mocked_init_url.start() mocked_init_url.return_value = "sqlite:///%s" % tmpfile self.projector = ProjectorDB() def find_record_by_ip_test(self): """ Test find record by IP """ # GIVEN: Record entries in database add_records(self, [TEST1_DATA, TEST2_DATA]) # WHEN: Search for record using IP record = self.projector.get_projector_by_ip(TEST2_DATA.ip) # THEN: Verify proper record returned self.assertTrue(compare_data(TEST2_DATA, record), "Record found should have been test_2 data") def find_record_by_name_test(self): """ Test find record by name """ # GIVEN: Record entries in database add_records(self, [TEST1_DATA, TEST2_DATA]) # WHEN: Search for record using name record = self.projector.get_projector_by_name(TEST2_DATA.name) # THEN: Verify proper record returned self.assertTrue(compare_data(TEST2_DATA, record), "Record found should have been test_2 data") def record_delete_test(self): """ Test record can be deleted """ # GIVEN: Record in database add_records(self, [TEST3_DATA]) record = self.projector.get_projector_by_ip(TEST3_DATA.ip) # WHEN: Record deleted self.projector.delete_projector(record) # THEN: Verify record not retrievable found = self.projector.get_projector_by_ip(TEST3_DATA.ip) self.assertFalse(found, "test_3 record should have been deleted") def record_edit_test(self): """ Test edited record returns the same record ID with different data """ # GIVEN: Record entries in database add_records(self, [TEST1_DATA, TEST2_DATA]) # WHEN: We retrieve a specific record record = self.projector.get_projector_by_ip(TEST1_DATA.ip) record_id = record.id # WHEN: Data is changed record.ip = TEST3_DATA.ip record.port = TEST3_DATA.port record.pin = TEST3_DATA.pin record.name = TEST3_DATA.name record.location = TEST3_DATA.location record.notes = TEST3_DATA.notes updated = self.projector.update_projector(record) self.assertTrue(updated, "Save updated record should have returned True") record = self.projector.get_projector_by_ip(TEST3_DATA.ip) # THEN: Record ID should remain the same, but data should be changed self.assertEqual(record_id, record.id, "Edited record should have the same ID") self.assertTrue(compare_data(TEST3_DATA, record), "Edited record should have new data") def source_add_test(self): """ Test source entry for projector item """ # GIVEN: Record entries in database self.projector.add_projector(TEST1_DATA) item = self.projector.get_projector_by_id(TEST1_DATA.id) item_id = item.id # WHEN: A source entry is saved for item source = ProjectorSource(projector_id=item_id, code="11", text="First RGB source") self.projector.add_source(source) # THEN: Projector should have the same source entry item = self.projector.get_projector_by_id(item_id) self.assertTrue(compare_source(item.source_list[0], source))
class ProjectorManager(OpenLPMixin, RegistryMixin, QWidget, Ui_ProjectorManager, RegistryProperties): """ Manage the projectors. """ def __init__(self, parent=None, projectordb=None): """ Basic initialization. :param parent: Who I belong to. :param projectordb: Database session inherited from superclass. """ log.debug('__init__()') super().__init__(parent) self.settings_section = 'projector' self.projectordb = projectordb self.projector_list = [] self.source_select_form = None def bootstrap_initialise(self): """ Pre-initialize setups. """ self.setup_ui(self) if self.projectordb is None: # Work around for testing creating a ~/.openlp.data.projector.projector.sql file log.debug('Creating new ProjectorDB() instance') self.projectordb = ProjectorDB() else: log.debug('Using existing ProjectorDB() instance') self.get_settings() def bootstrap_post_set_up(self): """ Post-initialize setups. """ # Set 1.5 second delay before loading all projectors if self.autostart: log.debug('Delaying 1.5 seconds before loading all projectors') QtCore.QTimer().singleShot(1500, self._load_projectors) else: log.debug('Loading all projectors') self._load_projectors() self.projector_form = ProjectorEditForm(self, projectordb=self.projectordb) self.projector_form.newProjector.connect( self.add_projector_from_wizard) self.projector_form.editProjector.connect( self.edit_projector_from_wizard) self.projector_list_widget.itemSelectionChanged.connect( self.update_icons) def get_settings(self): """ Retrieve the saved settings """ settings = Settings() settings.beginGroup(self.settings_section) self.autostart = settings.value('connect on start') self.poll_time = settings.value('poll time') self.socket_timeout = settings.value('socket timeout') self.source_select_dialog_type = settings.value('source dialog type') settings.endGroup() del settings def context_menu(self, point): """ Build the Right Click Context menu and set state. :param point: The position of the mouse so the correct item can be found. """ # QListWidgetItem to build menu for. item = self.projector_list_widget.itemAt(point) if item is None: return real_projector = item.data(QtCore.Qt.UserRole) projector_name = str(item.text()) visible = real_projector.link.status_connect >= S_CONNECTED log.debug('(%s) Building menu - visible = %s' % (projector_name, visible)) self.delete_action.setVisible(True) self.edit_action.setVisible(True) self.connect_action.setVisible(not visible) self.disconnect_action.setVisible(visible) self.status_action.setVisible(visible) if visible: self.select_input_action.setVisible( real_projector.link.power == S_ON) self.edit_input_action.setVisible( real_projector.link.power == S_ON) self.poweron_action.setVisible( real_projector.link.power == S_STANDBY) self.poweroff_action.setVisible(real_projector.link.power == S_ON) self.blank_action.setVisible(real_projector.link.power == S_ON and not real_projector.link.shutter) self.show_action.setVisible(real_projector.link.power == S_ON and real_projector.link.shutter) else: self.select_input_action.setVisible(False) self.edit_input_action.setVisible(False) self.poweron_action.setVisible(False) self.poweroff_action.setVisible(False) self.blank_action.setVisible(False) self.show_action.setVisible(False) self.menu.projector = real_projector self.menu.exec_(self.projector_list_widget.mapToGlobal(point)) def on_edit_input(self, opt=None): self.on_select_input(opt=opt, edit=True) def on_select_input(self, opt=None, edit=False): """ Builds menu for 'Select Input' option, then calls the selected projector item to change input source. :param opt: Needed by PyQt4 """ self.get_settings() # In case the dialog interface setting was changed list_item = self.projector_list_widget.item( self.projector_list_widget.currentRow()) projector = list_item.data(QtCore.Qt.UserRole) # QTabwidget for source select source = 100 while source > 99: if self.source_select_dialog_type == DialogSourceStyle.Tabbed: source_select_form = SourceSelectTabs( parent=self, projectordb=self.projectordb, edit=edit) else: source_select_form = SourceSelectSingle( parent=self, projectordb=self.projectordb, edit=edit) source = source_select_form.exec_(projector.link) log.debug('(%s) source_select_form() returned %s' % (projector.link.ip, source)) if source is not None and source > 0: projector.link.set_input_source(str(source)) return def on_add_projector(self, opt=None): """ Calls edit dialog to add a new projector to the database :param opt: Needed by PyQt4 """ self.projector_form.exec_() def on_blank_projector(self, opt=None): """ Calls projector thread to send blank screen command :param opt: Needed by PyQt4 """ try: ip = opt.link.ip projector = opt projector.link.set_shutter_closed() except AttributeError: for list_item in self.projector_list_widget.selectedItems(): if list_item is None: return projector = list_item.data(QtCore.Qt.UserRole) try: projector.link.set_shutter_closed() except: continue def on_doubleclick_item(self, item, opt=None): """ When item is doubleclicked, will connect to projector. :param item: List widget item for connection. :param opt: Needed by PyQt4 """ projector = item.data(QtCore.Qt.UserRole) if projector.link.state() != projector.link.ConnectedState: try: projector.link.connect_to_host() except: pass return def on_connect_projector(self, opt=None): """ Calls projector thread to connect to projector :param opt: Needed by PyQt4 """ try: ip = opt.link.ip projector = opt projector.link.connect_to_host() except AttributeError: for list_item in self.projector_list_widget.selectedItems(): if list_item is None: return projector = list_item.data(QtCore.Qt.UserRole) try: projector.link.connect_to_host() except: continue def on_delete_projector(self, opt=None): """ Deletes a projector from the list and the database :param opt: Needed by PyQt4 """ list_item = self.projector_list_widget.item( self.projector_list_widget.currentRow()) if list_item is None: return projector = list_item.data(QtCore.Qt.UserRole) msg = QtGui.QMessageBox() msg.setText('Delete projector (%s) %s?' % (projector.link.ip, projector.link.name)) msg.setInformativeText( 'Are you sure you want to delete this projector?') msg.setStandardButtons(msg.Cancel | msg.Ok) msg.setDefaultButton(msg.Cancel) ans = msg.exec_() if ans == msg.Cancel: return try: projector.link.projectorNetwork.disconnect(self.update_status) except (AttributeError, TypeError): pass try: projector.link.changeStatus.disconnect(self.update_status) except (AttributeError, TypeError): pass try: projector.link.authentication_error.disconnect( self.authentication_error) except (AttributeError, TypeError): pass try: projector.link.no_authentication_error.disconnect( self.no_authentication_error) except (AttributeError, TypeError): pass try: projector.link.projectorUpdateIcons.disconnect(self.update_icons) except (AttributeError, TypeError): pass try: projector.timer.stop() projector.timer.timeout.disconnect(projector.link.poll_loop) except (AttributeError, TypeError): pass try: projector.socket_timer.stop() projector.socket_timer.timeout.disconnect( projector.link.socket_abort) except (AttributeError, TypeError): pass projector.thread.quit() new_list = [] for item in self.projector_list: if item.link.dbid == projector.link.dbid: continue new_list.append(item) self.projector_list = new_list list_item = self.projector_list_widget.takeItem( self.projector_list_widget.currentRow()) list_item = None deleted = self.projectordb.delete_projector(projector.db_item) for item in self.projector_list: log.debug('New projector list - item: %s %s' % (item.link.ip, item.link.name)) def on_disconnect_projector(self, opt=None): """ Calls projector thread to disconnect from projector :param opt: Needed by PyQt4 """ try: ip = opt.link.ip projector = opt projector.link.disconnect_from_host() except AttributeError: for list_item in self.projector_list_widget.selectedItems(): if list_item is None: return projector = list_item.data(QtCore.Qt.UserRole) try: projector.link.disconnect_from_host() except: continue def on_edit_projector(self, opt=None): """ Calls edit dialog with selected projector to edit information :param opt: Needed by PyQt4 """ list_item = self.projector_list_widget.item( self.projector_list_widget.currentRow()) projector = list_item.data(QtCore.Qt.UserRole) if projector is None: return self.old_projector = projector projector.link.disconnect_from_host() self.projector_form.exec_(projector.db_item) projector.db_item = self.projectordb.get_projector_by_id( self.old_projector.db_item.id) def on_poweroff_projector(self, opt=None): """ Calls projector link to send Power Off command :param opt: Needed by PyQt4 """ try: ip = opt.link.ip projector = opt projector.link.set_power_off() except AttributeError: for list_item in self.projector_list_widget.selectedItems(): if list_item is None: return projector = list_item.data(QtCore.Qt.UserRole) try: projector.link.set_power_off() except: continue def on_poweron_projector(self, opt=None): """ Calls projector link to send Power On command :param opt: Needed by PyQt4 """ try: ip = opt.link.ip projector = opt projector.link.set_power_on() except AttributeError: for list_item in self.projector_list_widget.selectedItems(): if list_item is None: return projector = list_item.data(QtCore.Qt.UserRole) try: projector.link.set_power_on() except: continue def on_show_projector(self, opt=None): """ Calls projector thread to send open shutter command :param opt: Needed by PyQt4 """ try: ip = opt.link.ip projector = opt projector.link.set_shutter_open() except AttributeError: for list_item in self.projector_list_widget.selectedItems(): if list_item is None: return projector = list_item.data(QtCore.Qt.UserRole) try: projector.link.set_shutter_open() except: continue def on_status_projector(self, opt=None): """ Builds message box with projector status information :param opt: Needed by PyQt4 """ lwi = self.projector_list_widget.item( self.projector_list_widget.currentRow()) projector = lwi.data(QtCore.Qt.UserRole) message = '<b>%s</b>: %s<BR />' % (translate( 'OpenLP.ProjectorManager', 'Name'), projector.link.name) message = '%s<b>%s</b>: %s<br />' % ( message, translate('OpenLP.ProjectorManager', 'IP'), projector.link.ip) message = '%s<b>%s</b>: %s<br />' % ( message, translate('OpenLP.ProjectorManager', 'Port'), projector.link.port) message = '%s<b>%s</b>: %s<br />' % ( message, translate('OpenLP.ProjectorManager', 'Notes'), projector.link.notes) message = '%s<hr /><br >' % message if projector.link.manufacturer is None: message = '%s%s' % ( message, translate('OpenLP.ProjectorManager', 'Projector information not available at this time.')) else: message = '%s<b>%s</b>: %s<BR />' % ( message, translate( 'OpenLP.ProjectorManager', 'Projector Name'), projector.link.pjlink_name) message = '%s<b>%s</b>: %s<br />' % ( message, translate('OpenLP.ProjectorManager', 'Manufacturer'), projector.link.manufacturer) message = '%s<b>%s</b>: %s<br />' % ( message, translate('OpenLP.ProjectorManager', 'Model'), projector.link.model) message = '%s<b>%s</b>: %s<br /><br />' % ( message, translate('OpenLP.ProjectorManager', 'Other info'), projector.link.other_info) message = '%s<b>%s</b>: %s<br />' % ( message, translate('OpenLP.ProjectorManager', 'Power status'), ERROR_MSG[projector.link.power]) message = '%s<b>%s</b>: %s<br />' % ( message, translate('OpenLP.ProjectorManager', 'Shutter is'), translate('OpenLP.ProjectorManager', 'Closed') if projector.link.shutter else translate('OpenLP', 'Open')) message = '%s<b>%s</b>: %s<br />' % ( message, translate('OpenLP.ProjectorManager', 'Current source input is'), projector.link.source) count = 1 for item in projector.link.lamp: message = '%s <b>%s %s</b> (%s) %s: %s<br />' % ( message, translate('OpenLP.ProjectorManager', 'Lamp'), count, translate('OpenLP.ProjectorManager', 'On') if item['On'] else translate('OpenLP.ProjectorManager', 'Off'), translate('OpenLP.ProjectorManager', 'Hours'), item['Hours']) count = count + 1 message = '%s<hr /><br />' % message if projector.link.projector_errors is None: message = '%s%s' % (message, translate('OpenLP.ProjectorManager', 'No current errors or warnings')) else: message = '%s<b>%s</b>' % (message, translate( 'OpenLP.ProjectorManager', 'Current errors/warnings')) for (key, val) in projector.link.projector_errors.items(): message = '%s<b>%s</b>: %s<br />' % (message, key, ERROR_MSG[val]) QtGui.QMessageBox.information( self, translate('OpenLP.ProjectorManager', 'Projector Information'), message) def _add_projector(self, projector): """ Helper app to build a projector instance :param projector: Dict of projector database information :returns: PJLink1() instance """ log.debug('_add_projector()') return PJLink1(dbid=projector.id, ip=projector.ip, port=int(projector.port), name=projector.name, location=projector.location, notes=projector.notes, pin=None if projector.pin == '' else projector.pin, poll_time=self.poll_time, socket_timeout=self.socket_timeout) def add_projector(self, projector, start=False): """ Builds manager list item, projector thread, and timer for projector instance. :param projector: Projector instance to add :param start: Start projector if True """ item = ProjectorItem(link=self._add_projector(projector)) item.db_item = projector icon = QtGui.QIcon(QtGui.QPixmap(STATUS_ICONS[S_NOT_CONNECTED])) item.icon = icon widget = QtGui.QListWidgetItem(icon, item.link.name, self.projector_list_widget) widget.setData(QtCore.Qt.UserRole, item) item.link.db_item = item.db_item item.widget = widget thread = QThread(parent=self) thread.my_parent = self item.moveToThread(thread) thread.started.connect(item.link.thread_started) thread.finished.connect(item.link.thread_stopped) thread.finished.connect(thread.deleteLater) item.link.projectorNetwork.connect(self.update_status) item.link.changeStatus.connect(self.update_status) item.link.projectorAuthentication.connect(self.authentication_error) item.link.projectorNoAuthentication.connect( self.no_authentication_error) item.link.projectorUpdateIcons.connect(self.update_icons) timer = QtCore.QTimer(self) timer.setInterval(self.poll_time) timer.timeout.connect(item.link.poll_loop) item.timer = timer # Timeout in case of brain-dead projectors or cable disconnected socket_timer = QtCore.QTimer(self) socket_timer.setInterval(11000) socket_timer.timeout.connect(item.link.socket_abort) item.socket_timer = socket_timer thread.start() item.thread = thread item.link.timer = timer item.link.socket_timer = socket_timer item.link.widget = item.widget self.projector_list.append(item) if start: item.link.connect_to_host() for item in self.projector_list: log.debug('New projector list - item: (%s) %s' % (item.link.ip, item.link.name)) @pyqtSlot(str) def add_projector_from_wizard(self, ip, opts=None): """ Add a projector from the edit dialog :param ip: IP address of new record item to find :param opts: Needed by PyQt4 """ log.debug('add_projector_from_wizard(ip=%s)' % ip) item = self.projectordb.get_projector_by_ip(ip) self.add_projector(item) @pyqtSlot(object) def edit_projector_from_wizard(self, projector): """ Update projector from the wizard edit page :param projector: Projector() instance of projector with updated information """ log.debug('edit_projector_from_wizard(ip=%s)' % projector.ip) self.old_projector.link.name = projector.name self.old_projector.link.ip = projector.ip self.old_projector.link.pin = None if projector.pin == '' else projector.pin self.old_projector.link.port = projector.port self.old_projector.link.location = projector.location self.old_projector.link.notes = projector.notes self.old_projector.widget.setText(projector.name) def _load_projectors(self): """' Load projectors - only call when initializing """ log.debug('_load_projectors()') self.projector_list_widget.clear() for item in self.projectordb.get_projector_all(): self.add_projector(projector=item, start=self.autostart) def get_projector_list(self): """ Return the list of active projectors :returns: list """ return self.projector_list @pyqtSlot(str, int, str) def update_status(self, ip, status=None, msg=None): """ Update the status information/icon for selected list item :param ip: IP address of projector :param status: Optional status code :param msg: Optional status message """ if status is None: return item = None for list_item in self.projector_list: if ip == list_item.link.ip: item = list_item break message = translate('OpenLP.ProjectorManager', 'No message') if msg is None else msg if status in STATUS_STRING: status_code = STATUS_STRING[status] message = ERROR_MSG[status] if msg is None else msg elif status in ERROR_STRING: status_code = ERROR_STRING[status] message = ERROR_MSG[status] if msg is None else msg else: status_code = status message = ERROR_MSG[status] if msg is None else msg log.debug('(%s) updateStatus(status=%s) message: "%s"' % (item.link.name, status_code, message)) if status in STATUS_ICONS: if item.status == status: return item.status = status item.icon = QtGui.QIcon(QtGui.QPixmap(STATUS_ICONS[status])) if status in ERROR_STRING: status_code = ERROR_STRING[status] elif status in STATUS_STRING: status_code = STATUS_STRING[status] log.debug('(%s) Updating icon with %s' % (item.link.name, status_code)) item.widget.setIcon(item.icon) self.update_icons() def get_toolbar_item(self, name, enabled=False, hidden=False): item = self.one_toolbar.findChild(QtGui.QAction, name) if item == 0: log.debug('No item found with name "%s"' % name) return item.setVisible(False if hidden else True) item.setEnabled(True if enabled else False) @pyqtSlot() def update_icons(self): """ Update the icons when the selected projectors change """ count = len(self.projector_list_widget.selectedItems()) projector = None if count == 0: self.get_toolbar_item('edit_projector') self.get_toolbar_item('delete_projector') self.get_toolbar_item('view_projector', hidden=True) self.get_toolbar_item('source_view_projector', hidden=True) self.get_toolbar_item('connect_projector') self.get_toolbar_item('disconnect_projector') self.get_toolbar_item('poweron_projector') self.get_toolbar_item('poweroff_projector') self.get_toolbar_item('blank_projector') self.get_toolbar_item('show_projector') self.get_toolbar_item('connect_projector_multiple', hidden=True) self.get_toolbar_item('disconnect_projector_multiple', hidden=True) self.get_toolbar_item('poweron_projector_multiple', hidden=True) self.get_toolbar_item('poweroff_projector_multiple', hidden=True) self.get_toolbar_item('blank_projector_multiple', hidden=True) self.get_toolbar_item('show_projector_multiple', hidden=True) elif count == 1: projector = self.projector_list_widget.selectedItems()[0].data( QtCore.Qt.UserRole) connected = projector.link.state() == projector.link.ConnectedState power = projector.link.power == S_ON self.get_toolbar_item('connect_projector_multiple', hidden=True) self.get_toolbar_item('disconnect_projector_multiple', hidden=True) self.get_toolbar_item('poweron_projector_multiple', hidden=True) self.get_toolbar_item('poweroff_projector_multiple', hidden=True) self.get_toolbar_item('blank_projector_multiple', hidden=True) self.get_toolbar_item('show_projector_multiple', hidden=True) if connected: self.get_toolbar_item('view_projector', enabled=True) self.get_toolbar_item( 'source_view_projector', enabled=connected and power and projector.link.source_available is not None) self.get_toolbar_item('edit_projector', hidden=True) self.get_toolbar_item('delete_projector', hidden=True) else: self.get_toolbar_item('view_projector', hidden=True) self.get_toolbar_item('source_view_projector', hidden=True) self.get_toolbar_item('edit_projector', enabled=True) self.get_toolbar_item('delete_projector', enabled=True) self.get_toolbar_item('connect_projector', enabled=not connected) self.get_toolbar_item('disconnect_projector', enabled=connected) self.get_toolbar_item('poweron_projector', enabled=connected and (projector.link.power == S_STANDBY)) self.get_toolbar_item('poweroff_projector', enabled=connected and (projector.link.power == S_ON)) if projector.link.shutter is not None: self.get_toolbar_item('blank_projector', enabled=(connected and power and not projector.link.shutter)) self.get_toolbar_item('show_projector', enabled=(connected and power and projector.link.shutter)) else: self.get_toolbar_item('blank_projector', enabled=False) self.get_toolbar_item('show_projector', enabled=False) else: self.get_toolbar_item('edit_projector', enabled=False) self.get_toolbar_item('delete_projector', enabled=False) self.get_toolbar_item('view_projector', hidden=True) self.get_toolbar_item('source_view_projector', hidden=True) self.get_toolbar_item('connect_projector', hidden=True) self.get_toolbar_item('disconnect_projector', hidden=True) self.get_toolbar_item('poweron_projector', hidden=True) self.get_toolbar_item('poweroff_projector', hidden=True) self.get_toolbar_item('blank_projector', hidden=True) self.get_toolbar_item('show_projector', hidden=True) self.get_toolbar_item('connect_projector_multiple', hidden=False, enabled=True) self.get_toolbar_item('disconnect_projector_multiple', hidden=False, enabled=True) self.get_toolbar_item('poweron_projector_multiple', hidden=False, enabled=True) self.get_toolbar_item('poweroff_projector_multiple', hidden=False, enabled=True) self.get_toolbar_item('blank_projector_multiple', hidden=False, enabled=True) self.get_toolbar_item('show_projector_multiple', hidden=False, enabled=True) @pyqtSlot(str) def authentication_error(self, name): """ Display warning dialog when attempting to connect with invalid pin :param name: Name from QListWidgetItem """ QtGui.QMessageBox.warning( self, translate('OpenLP.ProjectorManager', '"%s" Authentication Error' % name), '<br />There was an authentication error while trying to connect.' '<br /><br />Please verify your PIN setting ' 'for projector item "%s"' % name) @pyqtSlot(str) def no_authentication_error(self, name): """ Display warning dialog when pin saved for item but projector does not require pin. :param name: Name from QListWidgetItem """ QtGui.QMessageBox.warning( self, translate('OpenLP.ProjectorManager', '"%s" No Authentication Error' % name), '<br />PIN is set and projector does not require authentication.' '<br /><br />Please verify your PIN setting ' 'for projector item "%s"' % name)
class ProjectorManager(OpenLPMixin, RegistryMixin, QWidget, Ui_ProjectorManager, RegistryProperties): """ Manage the projectors. """ def __init__(self, parent=None, projectordb=None): """ Basic initialization. :param parent: Who I belong to. :param projectordb: Database session inherited from superclass. """ log.debug('__init__()') super().__init__(parent) self.settings_section = 'projector' self.projectordb = projectordb self.projector_list = [] self.source_select_form = None def bootstrap_initialise(self): """ Pre-initialize setups. """ self.setup_ui(self) if self.projectordb is None: # Work around for testing creating a ~/.openlp.data.projector.projector.sql file log.debug('Creating new ProjectorDB() instance') self.projectordb = ProjectorDB() else: log.debug('Using existing ProjectorDB() instance') self.get_settings() def bootstrap_post_set_up(self): """ Post-initialize setups. """ # Set 1.5 second delay before loading all projectors if self.autostart: log.debug('Delaying 1.5 seconds before loading all projectors') QtCore.QTimer().singleShot(1500, self._load_projectors) else: log.debug('Loading all projectors') self._load_projectors() self.projector_form = ProjectorEditForm(self, projectordb=self.projectordb) self.projector_form.newProjector.connect(self.add_projector_from_wizard) self.projector_form.editProjector.connect(self.edit_projector_from_wizard) self.projector_list_widget.itemSelectionChanged.connect(self.update_icons) def get_settings(self): """ Retrieve the saved settings """ settings = Settings() settings.beginGroup(self.settings_section) self.autostart = settings.value('connect on start') self.poll_time = settings.value('poll time') self.socket_timeout = settings.value('socket timeout') self.source_select_dialog_type = settings.value('source dialog type') settings.endGroup() del settings def context_menu(self, point): """ Build the Right Click Context menu and set state. :param point: The position of the mouse so the correct item can be found. """ # QListWidgetItem to build menu for. item = self.projector_list_widget.itemAt(point) if item is None: return real_projector = item.data(QtCore.Qt.UserRole) projector_name = str(item.text()) visible = real_projector.link.status_connect >= S_CONNECTED log.debug('(%s) Building menu - visible = %s' % (projector_name, visible)) self.delete_action.setVisible(True) self.edit_action.setVisible(True) self.connect_action.setVisible(not visible) self.disconnect_action.setVisible(visible) self.status_action.setVisible(visible) if visible: self.select_input_action.setVisible(real_projector.link.power == S_ON) self.edit_input_action.setVisible(real_projector.link.power == S_ON) self.poweron_action.setVisible(real_projector.link.power == S_STANDBY) self.poweroff_action.setVisible(real_projector.link.power == S_ON) self.blank_action.setVisible(real_projector.link.power == S_ON and not real_projector.link.shutter) self.show_action.setVisible(real_projector.link.power == S_ON and real_projector.link.shutter) else: self.select_input_action.setVisible(False) self.edit_input_action.setVisible(False) self.poweron_action.setVisible(False) self.poweroff_action.setVisible(False) self.blank_action.setVisible(False) self.show_action.setVisible(False) self.menu.projector = real_projector self.menu.exec_(self.projector_list_widget.mapToGlobal(point)) def on_edit_input(self, opt=None): self.on_select_input(opt=opt, edit=True) def on_select_input(self, opt=None, edit=False): """ Builds menu for 'Select Input' option, then calls the selected projector item to change input source. :param opt: Needed by PyQt4 """ self.get_settings() # In case the dialog interface setting was changed list_item = self.projector_list_widget.item(self.projector_list_widget.currentRow()) projector = list_item.data(QtCore.Qt.UserRole) # QTabwidget for source select source = 100 while source > 99: if self.source_select_dialog_type == DialogSourceStyle.Tabbed: source_select_form = SourceSelectTabs(parent=self, projectordb=self.projectordb, edit=edit) else: source_select_form = SourceSelectSingle(parent=self, projectordb=self.projectordb, edit=edit) source = source_select_form.exec_(projector.link) log.debug('(%s) source_select_form() returned %s' % (projector.link.ip, source)) if source is not None and source > 0: projector.link.set_input_source(str(source)) return def on_add_projector(self, opt=None): """ Calls edit dialog to add a new projector to the database :param opt: Needed by PyQt4 """ self.projector_form.exec_() def on_blank_projector(self, opt=None): """ Calls projector thread to send blank screen command :param opt: Needed by PyQt4 """ try: ip = opt.link.ip projector = opt projector.link.set_shutter_closed() except AttributeError: for list_item in self.projector_list_widget.selectedItems(): if list_item is None: return projector = list_item.data(QtCore.Qt.UserRole) try: projector.link.set_shutter_closed() except: continue def on_doubleclick_item(self, item, opt=None): """ When item is doubleclicked, will connect to projector. :param item: List widget item for connection. :param opt: Needed by PyQt4 """ projector = item.data(QtCore.Qt.UserRole) if projector.link.state() != projector.link.ConnectedState: try: projector.link.connect_to_host() except: pass return def on_connect_projector(self, opt=None): """ Calls projector thread to connect to projector :param opt: Needed by PyQt4 """ try: ip = opt.link.ip projector = opt projector.link.connect_to_host() except AttributeError: for list_item in self.projector_list_widget.selectedItems(): if list_item is None: return projector = list_item.data(QtCore.Qt.UserRole) try: projector.link.connect_to_host() except: continue def on_delete_projector(self, opt=None): """ Deletes a projector from the list and the database :param opt: Needed by PyQt4 """ list_item = self.projector_list_widget.item(self.projector_list_widget.currentRow()) if list_item is None: return projector = list_item.data(QtCore.Qt.UserRole) msg = QtGui.QMessageBox() msg.setText('Delete projector (%s) %s?' % (projector.link.ip, projector.link.name)) msg.setInformativeText('Are you sure you want to delete this projector?') msg.setStandardButtons(msg.Cancel | msg.Ok) msg.setDefaultButton(msg.Cancel) ans = msg.exec_() if ans == msg.Cancel: return try: projector.link.projectorNetwork.disconnect(self.update_status) except (AttributeError, TypeError): pass try: projector.link.changeStatus.disconnect(self.update_status) except (AttributeError, TypeError): pass try: projector.link.authentication_error.disconnect(self.authentication_error) except (AttributeError, TypeError): pass try: projector.link.no_authentication_error.disconnect(self.no_authentication_error) except (AttributeError, TypeError): pass try: projector.link.projectorUpdateIcons.disconnect(self.update_icons) except (AttributeError, TypeError): pass try: projector.timer.stop() projector.timer.timeout.disconnect(projector.link.poll_loop) except (AttributeError, TypeError): pass try: projector.socket_timer.stop() projector.socket_timer.timeout.disconnect(projector.link.socket_abort) except (AttributeError, TypeError): pass projector.thread.quit() new_list = [] for item in self.projector_list: if item.link.dbid == projector.link.dbid: continue new_list.append(item) self.projector_list = new_list list_item = self.projector_list_widget.takeItem(self.projector_list_widget.currentRow()) list_item = None deleted = self.projectordb.delete_projector(projector.db_item) for item in self.projector_list: log.debug('New projector list - item: %s %s' % (item.link.ip, item.link.name)) def on_disconnect_projector(self, opt=None): """ Calls projector thread to disconnect from projector :param opt: Needed by PyQt4 """ try: ip = opt.link.ip projector = opt projector.link.disconnect_from_host() except AttributeError: for list_item in self.projector_list_widget.selectedItems(): if list_item is None: return projector = list_item.data(QtCore.Qt.UserRole) try: projector.link.disconnect_from_host() except: continue def on_edit_projector(self, opt=None): """ Calls edit dialog with selected projector to edit information :param opt: Needed by PyQt4 """ list_item = self.projector_list_widget.item(self.projector_list_widget.currentRow()) projector = list_item.data(QtCore.Qt.UserRole) if projector is None: return self.old_projector = projector projector.link.disconnect_from_host() self.projector_form.exec_(projector.db_item) projector.db_item = self.projectordb.get_projector_by_id(self.old_projector.db_item.id) def on_poweroff_projector(self, opt=None): """ Calls projector link to send Power Off command :param opt: Needed by PyQt4 """ try: ip = opt.link.ip projector = opt projector.link.set_power_off() except AttributeError: for list_item in self.projector_list_widget.selectedItems(): if list_item is None: return projector = list_item.data(QtCore.Qt.UserRole) try: projector.link.set_power_off() except: continue def on_poweron_projector(self, opt=None): """ Calls projector link to send Power On command :param opt: Needed by PyQt4 """ try: ip = opt.link.ip projector = opt projector.link.set_power_on() except AttributeError: for list_item in self.projector_list_widget.selectedItems(): if list_item is None: return projector = list_item.data(QtCore.Qt.UserRole) try: projector.link.set_power_on() except: continue def on_show_projector(self, opt=None): """ Calls projector thread to send open shutter command :param opt: Needed by PyQt4 """ try: ip = opt.link.ip projector = opt projector.link.set_shutter_open() except AttributeError: for list_item in self.projector_list_widget.selectedItems(): if list_item is None: return projector = list_item.data(QtCore.Qt.UserRole) try: projector.link.set_shutter_open() except: continue def on_status_projector(self, opt=None): """ Builds message box with projector status information :param opt: Needed by PyQt4 """ lwi = self.projector_list_widget.item(self.projector_list_widget.currentRow()) projector = lwi.data(QtCore.Qt.UserRole) message = '<b>%s</b>: %s<BR />' % (translate('OpenLP.ProjectorManager', 'Name'), projector.link.name) message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'IP'), projector.link.ip) message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Port'), projector.link.port) message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Notes'), projector.link.notes) message = '%s<hr /><br >' % message if projector.link.manufacturer is None: message = '%s%s' % (message, translate('OpenLP.ProjectorManager', 'Projector information not available at this time.')) else: message = '%s<b>%s</b>: %s<BR />' % (message, translate('OpenLP.ProjectorManager', 'Projector Name'), projector.link.pjlink_name) message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Manufacturer'), projector.link.manufacturer) message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Model'), projector.link.model) message = '%s<b>%s</b>: %s<br /><br />' % (message, translate('OpenLP.ProjectorManager', 'Other info'), projector.link.other_info) message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Power status'), ERROR_MSG[projector.link.power]) message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Shutter is'), translate('OpenLP.ProjectorManager', 'Closed') if projector.link.shutter else translate('OpenLP', 'Open')) message = '%s<b>%s</b>: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Current source input is'), projector.link.source) count = 1 for item in projector.link.lamp: message = '%s <b>%s %s</b> (%s) %s: %s<br />' % (message, translate('OpenLP.ProjectorManager', 'Lamp'), count, translate('OpenLP.ProjectorManager', 'On') if item['On'] else translate('OpenLP.ProjectorManager', 'Off'), translate('OpenLP.ProjectorManager', 'Hours'), item['Hours']) count = count + 1 message = '%s<hr /><br />' % message if projector.link.projector_errors is None: message = '%s%s' % (message, translate('OpenLP.ProjectorManager', 'No current errors or warnings')) else: message = '%s<b>%s</b>' % (message, translate('OpenLP.ProjectorManager', 'Current errors/warnings')) for (key, val) in projector.link.projector_errors.items(): message = '%s<b>%s</b>: %s<br />' % (message, key, ERROR_MSG[val]) QtGui.QMessageBox.information(self, translate('OpenLP.ProjectorManager', 'Projector Information'), message) def _add_projector(self, projector): """ Helper app to build a projector instance :param projector: Dict of projector database information :returns: PJLink1() instance """ log.debug('_add_projector()') return PJLink1(dbid=projector.id, ip=projector.ip, port=int(projector.port), name=projector.name, location=projector.location, notes=projector.notes, pin=None if projector.pin == '' else projector.pin, poll_time=self.poll_time, socket_timeout=self.socket_timeout ) def add_projector(self, projector, start=False): """ Builds manager list item, projector thread, and timer for projector instance. :param projector: Projector instance to add :param start: Start projector if True """ item = ProjectorItem(link=self._add_projector(projector)) item.db_item = projector icon = QtGui.QIcon(QtGui.QPixmap(STATUS_ICONS[S_NOT_CONNECTED])) item.icon = icon widget = QtGui.QListWidgetItem(icon, item.link.name, self.projector_list_widget ) widget.setData(QtCore.Qt.UserRole, item) item.link.db_item = item.db_item item.widget = widget thread = QThread(parent=self) thread.my_parent = self item.moveToThread(thread) thread.started.connect(item.link.thread_started) thread.finished.connect(item.link.thread_stopped) thread.finished.connect(thread.deleteLater) item.link.projectorNetwork.connect(self.update_status) item.link.changeStatus.connect(self.update_status) item.link.projectorAuthentication.connect(self.authentication_error) item.link.projectorNoAuthentication.connect(self.no_authentication_error) item.link.projectorUpdateIcons.connect(self.update_icons) timer = QtCore.QTimer(self) timer.setInterval(self.poll_time) timer.timeout.connect(item.link.poll_loop) item.timer = timer # Timeout in case of brain-dead projectors or cable disconnected socket_timer = QtCore.QTimer(self) socket_timer.setInterval(11000) socket_timer.timeout.connect(item.link.socket_abort) item.socket_timer = socket_timer thread.start() item.thread = thread item.link.timer = timer item.link.socket_timer = socket_timer item.link.widget = item.widget self.projector_list.append(item) if start: item.link.connect_to_host() for item in self.projector_list: log.debug('New projector list - item: (%s) %s' % (item.link.ip, item.link.name)) @pyqtSlot(str) def add_projector_from_wizard(self, ip, opts=None): """ Add a projector from the edit dialog :param ip: IP address of new record item to find :param opts: Needed by PyQt4 """ log.debug('add_projector_from_wizard(ip=%s)' % ip) item = self.projectordb.get_projector_by_ip(ip) self.add_projector(item) @pyqtSlot(object) def edit_projector_from_wizard(self, projector): """ Update projector from the wizard edit page :param projector: Projector() instance of projector with updated information """ log.debug('edit_projector_from_wizard(ip=%s)' % projector.ip) self.old_projector.link.name = projector.name self.old_projector.link.ip = projector.ip self.old_projector.link.pin = None if projector.pin == '' else projector.pin self.old_projector.link.port = projector.port self.old_projector.link.location = projector.location self.old_projector.link.notes = projector.notes self.old_projector.widget.setText(projector.name) def _load_projectors(self): """' Load projectors - only call when initializing """ log.debug('_load_projectors()') self.projector_list_widget.clear() for item in self.projectordb.get_projector_all(): self.add_projector(projector=item, start=self.autostart) def get_projector_list(self): """ Return the list of active projectors :returns: list """ return self.projector_list @pyqtSlot(str, int, str) def update_status(self, ip, status=None, msg=None): """ Update the status information/icon for selected list item :param ip: IP address of projector :param status: Optional status code :param msg: Optional status message """ if status is None: return item = None for list_item in self.projector_list: if ip == list_item.link.ip: item = list_item break message = translate('OpenLP.ProjectorManager', 'No message') if msg is None else msg if status in STATUS_STRING: status_code = STATUS_STRING[status] message = ERROR_MSG[status] if msg is None else msg elif status in ERROR_STRING: status_code = ERROR_STRING[status] message = ERROR_MSG[status] if msg is None else msg else: status_code = status message = ERROR_MSG[status] if msg is None else msg log.debug('(%s) updateStatus(status=%s) message: "%s"' % (item.link.name, status_code, message)) if status in STATUS_ICONS: if item.status == status: return item.status = status item.icon = QtGui.QIcon(QtGui.QPixmap(STATUS_ICONS[status])) if status in ERROR_STRING: status_code = ERROR_STRING[status] elif status in STATUS_STRING: status_code = STATUS_STRING[status] log.debug('(%s) Updating icon with %s' % (item.link.name, status_code)) item.widget.setIcon(item.icon) self.update_icons() def get_toolbar_item(self, name, enabled=False, hidden=False): item = self.one_toolbar.findChild(QtGui.QAction, name) if item == 0: log.debug('No item found with name "%s"' % name) return item.setVisible(False if hidden else True) item.setEnabled(True if enabled else False) @pyqtSlot() def update_icons(self): """ Update the icons when the selected projectors change """ count = len(self.projector_list_widget.selectedItems()) projector = None if count == 0: self.get_toolbar_item('edit_projector') self.get_toolbar_item('delete_projector') self.get_toolbar_item('view_projector', hidden=True) self.get_toolbar_item('source_view_projector', hidden=True) self.get_toolbar_item('connect_projector') self.get_toolbar_item('disconnect_projector') self.get_toolbar_item('poweron_projector') self.get_toolbar_item('poweroff_projector') self.get_toolbar_item('blank_projector') self.get_toolbar_item('show_projector') self.get_toolbar_item('connect_projector_multiple', hidden=True) self.get_toolbar_item('disconnect_projector_multiple', hidden=True) self.get_toolbar_item('poweron_projector_multiple', hidden=True) self.get_toolbar_item('poweroff_projector_multiple', hidden=True) self.get_toolbar_item('blank_projector_multiple', hidden=True) self.get_toolbar_item('show_projector_multiple', hidden=True) elif count == 1: projector = self.projector_list_widget.selectedItems()[0].data(QtCore.Qt.UserRole) connected = projector.link.state() == projector.link.ConnectedState power = projector.link.power == S_ON self.get_toolbar_item('connect_projector_multiple', hidden=True) self.get_toolbar_item('disconnect_projector_multiple', hidden=True) self.get_toolbar_item('poweron_projector_multiple', hidden=True) self.get_toolbar_item('poweroff_projector_multiple', hidden=True) self.get_toolbar_item('blank_projector_multiple', hidden=True) self.get_toolbar_item('show_projector_multiple', hidden=True) if connected: self.get_toolbar_item('view_projector', enabled=True) self.get_toolbar_item('source_view_projector', enabled=connected and power and projector.link.source_available is not None) self.get_toolbar_item('edit_projector', hidden=True) self.get_toolbar_item('delete_projector', hidden=True) else: self.get_toolbar_item('view_projector', hidden=True) self.get_toolbar_item('source_view_projector', hidden=True) self.get_toolbar_item('edit_projector', enabled=True) self.get_toolbar_item('delete_projector', enabled=True) self.get_toolbar_item('connect_projector', enabled=not connected) self.get_toolbar_item('disconnect_projector', enabled=connected) self.get_toolbar_item('poweron_projector', enabled=connected and (projector.link.power == S_STANDBY)) self.get_toolbar_item('poweroff_projector', enabled=connected and (projector.link.power == S_ON)) if projector.link.shutter is not None: self.get_toolbar_item('blank_projector', enabled=(connected and power and not projector.link.shutter)) self.get_toolbar_item('show_projector', enabled=(connected and power and projector.link.shutter)) else: self.get_toolbar_item('blank_projector', enabled=False) self.get_toolbar_item('show_projector', enabled=False) else: self.get_toolbar_item('edit_projector', enabled=False) self.get_toolbar_item('delete_projector', enabled=False) self.get_toolbar_item('view_projector', hidden=True) self.get_toolbar_item('source_view_projector', hidden=True) self.get_toolbar_item('connect_projector', hidden=True) self.get_toolbar_item('disconnect_projector', hidden=True) self.get_toolbar_item('poweron_projector', hidden=True) self.get_toolbar_item('poweroff_projector', hidden=True) self.get_toolbar_item('blank_projector', hidden=True) self.get_toolbar_item('show_projector', hidden=True) self.get_toolbar_item('connect_projector_multiple', hidden=False, enabled=True) self.get_toolbar_item('disconnect_projector_multiple', hidden=False, enabled=True) self.get_toolbar_item('poweron_projector_multiple', hidden=False, enabled=True) self.get_toolbar_item('poweroff_projector_multiple', hidden=False, enabled=True) self.get_toolbar_item('blank_projector_multiple', hidden=False, enabled=True) self.get_toolbar_item('show_projector_multiple', hidden=False, enabled=True) @pyqtSlot(str) def authentication_error(self, name): """ Display warning dialog when attempting to connect with invalid pin :param name: Name from QListWidgetItem """ QtGui.QMessageBox.warning(self, translate('OpenLP.ProjectorManager', '"%s" Authentication Error' % name), '<br />There was an authentication error while trying to connect.' '<br /><br />Please verify your PIN setting ' 'for projector item "%s"' % name) @pyqtSlot(str) def no_authentication_error(self, name): """ Display warning dialog when pin saved for item but projector does not require pin. :param name: Name from QListWidgetItem """ QtGui.QMessageBox.warning(self, translate('OpenLP.ProjectorManager', '"%s" No Authentication Error' % name), '<br />PIN is set and projector does not require authentication.' '<br /><br />Please verify your PIN setting ' 'for projector item "%s"' % name)
class TestProjectorDB(TestCase): """ Test case for ProjectorDB """ def setUp(self): """ Set up anything necessary for all tests """ if not hasattr(self, 'projector'): with patch('openlp.core.lib.projector.db.init_url' ) as mocked_init_url: mocked_init_url.start() mocked_init_url.return_value = 'sqlite:///%s' % tmpfile self.projector = ProjectorDB() def find_record_by_ip_test(self): """ Test find record by IP """ # GIVEN: Record entries in database add_records(self, [TEST1_DATA, TEST2_DATA]) # WHEN: Search for record using IP record = self.projector.get_projector_by_ip(TEST2_DATA.ip) # THEN: Verify proper record returned self.assertTrue(compare_data(TEST2_DATA, record), 'Record found should have been test_2 data') def find_record_by_name_test(self): """ Test find record by name """ # GIVEN: Record entries in database add_records(self, [TEST1_DATA, TEST2_DATA]) # WHEN: Search for record using name record = self.projector.get_projector_by_name(TEST2_DATA.name) # THEN: Verify proper record returned self.assertTrue(compare_data(TEST2_DATA, record), 'Record found should have been test_2 data') def record_delete_test(self): """ Test record can be deleted """ # GIVEN: Record in database add_records(self, [ TEST3_DATA, ]) record = self.projector.get_projector_by_ip(TEST3_DATA.ip) # WHEN: Record deleted self.projector.delete_projector(record) # THEN: Verify record not retrievable found = self.projector.get_projector_by_ip(TEST3_DATA.ip) self.assertFalse(found, 'test_3 record should have been deleted') def record_edit_test(self): """ Test edited record returns the same record ID with different data """ # GIVEN: Record entries in database add_records(self, [TEST1_DATA, TEST2_DATA]) # WHEN: We retrieve a specific record record = self.projector.get_projector_by_ip(TEST1_DATA.ip) record_id = record.id # WHEN: Data is changed record.ip = TEST3_DATA.ip record.port = TEST3_DATA.port record.pin = TEST3_DATA.pin record.name = TEST3_DATA.name record.location = TEST3_DATA.location record.notes = TEST3_DATA.notes updated = self.projector.update_projector(record) self.assertTrue(updated, 'Save updated record should have returned True') record = self.projector.get_projector_by_ip(TEST3_DATA.ip) # THEN: Record ID should remain the same, but data should be changed self.assertEqual(record_id, record.id, 'Edited record should have the same ID') self.assertTrue(compare_data(TEST3_DATA, record), 'Edited record should have new data') def source_add_test(self): """ Test source entry for projector item """ # GIVEN: Record entries in database self.projector.add_projector(TEST1_DATA) item = self.projector.get_projector_by_id(TEST1_DATA.id) item_id = item.id # WHEN: A source entry is saved for item source = ProjectorSource(projector_id=item_id, code='11', text='First RGB source') self.projector.add_source(source) # THEN: Projector should have the same source entry item = self.projector.get_projector_by_id(item_id) self.assertTrue(compare_source(item.source_list[0], source))
class TestProjectorDB(TestCase): """ Test case for ProjectorDB """ @patch('openlp.core.lib.projector.db.init_url') def setUp(self, mocked_init_url): """ Set up anything necessary for all tests """ mocked_init_url.return_value = 'sqlite:///{db}'.format(db=TEST_DB) self.projector = ProjectorDB() def tearDown(self): """ Clean up """ self.projector.session.close() self.projector = None retries = 0 while retries < 5: try: if os.path.exists(TEST_DB): os.unlink(TEST_DB) break except: time.sleep(1) retries += 1 def test_find_record_by_ip(self): """ Test find record by IP """ # GIVEN: Record entries in database add_records(self.projector, [Projector(**TEST1_DATA), Projector(**TEST2_DATA)]) # WHEN: Search for record using IP record = self.projector.get_projector_by_ip(TEST2_DATA['ip']) # THEN: Verify proper record returned self.assertTrue(compare_data(Projector(**TEST2_DATA), record), 'Record found should have been test_2 data') def test_find_record_by_name(self): """ Test find record by name """ # GIVEN: Record entries in database add_records(self.projector, [Projector(**TEST1_DATA), Projector(**TEST2_DATA)]) # WHEN: Search for record using name record = self.projector.get_projector_by_name(TEST2_DATA['name']) # THEN: Verify proper record returned self.assertTrue(compare_data(Projector(**TEST2_DATA), record), 'Record found should have been test_2 data') def test_record_delete(self): """ Test record can be deleted """ # GIVEN: Record in database add_records(self.projector, [Projector(**TEST3_DATA), ]) record = self.projector.get_projector_by_ip(TEST3_DATA['ip']) # WHEN: Record deleted self.projector.delete_projector(record) # THEN: Verify record not retrievable found = self.projector.get_projector_by_ip(TEST3_DATA['ip']) self.assertFalse(found, 'test_3 record should have been deleted') def test_record_edit(self): """ Test edited record returns the same record ID with different data """ # GIVEN: Record entries in database add_records(self.projector, [Projector(**TEST1_DATA), Projector(**TEST2_DATA)]) # WHEN: We retrieve a specific record record = self.projector.get_projector_by_ip(TEST1_DATA['ip']) record_id = record.id # WHEN: Data is changed record.ip = TEST3_DATA['ip'] record.port = TEST3_DATA['port'] record.pin = TEST3_DATA['pin'] record.name = TEST3_DATA['name'] record.location = TEST3_DATA['location'] record.notes = TEST3_DATA['notes'] updated = self.projector.update_projector(record) self.assertTrue(updated, 'Save updated record should have returned True') record = self.projector.get_projector_by_ip(TEST3_DATA['ip']) # THEN: Record ID should remain the same, but data should be changed self.assertEqual(record_id, record.id, 'Edited record should have the same ID') self.assertTrue(compare_data(Projector(**TEST3_DATA), record), 'Edited record should have new data') def test_source_add(self): """ Test source entry for projector item """ # GIVEN: Record entries in database projector1 = Projector(**TEST1_DATA) self.projector.add_projector(projector1) item = self.projector.get_projector_by_id(projector1.id) item_id = item.id # WHEN: A source entry is saved for item source = ProjectorSource(projector_id=item_id, code='11', text='First RGB source') self.projector.add_source(source) # THEN: Projector should have the same source entry item = self.projector.get_projector_by_id(item_id) self.assertTrue(compare_source(item.source_list[0], source)) def test_manufacturer_repr(self): """ Test Manufacturer.__repr__() text """ # GIVEN: Test object manufacturer = Manufacturer() # WHEN: Name is set manufacturer.name = 'OpenLP Test' # THEN: __repr__ should return a proper string self.assertEqual(str(manufacturer), '<Manufacturer(name="OpenLP Test")>', 'Manufacturer.__repr__() should have returned a proper representation string') def test_model_repr(self): """ Test Model.__repr__() text """ # GIVEN: Test object model = Model() # WHEN: Name is set model.name = 'OpenLP Test' # THEN: __repr__ should return a proper string self.assertEqual(str(model), '<Model(name='"OpenLP Test"')>', 'Model.__repr__() should have returned a proper representation string') def test_source_repr(self): """ Test Source.__repr__() text """ # GIVEN: Test object source = Source() # WHEN: Source() information is set source.pjlink_name = 'Test object' source.pjlink_code = '11' source.text = 'Input text' # THEN: __repr__ should return a proper string self.assertEqual(str(source), '<Source(pjlink_name="Test object", pjlink_code="11", text="Input text")>', 'Source.__repr__() should have returned a proper representation string') def test_projector_repr(self): """ Test Projector.__repr__() text """ # GIVEN: Test object projector = Projector() # WHEN: projector() is populated # NOTE: projector.pin, projector.other, projector.sources should all return None # projector.source_list should return an empty list projector.id = 0 projector.ip = '127.0.0.1' projector.port = PJLINK_PORT projector.name = 'Test One' projector.location = 'Somewhere over the rainbow' projector.notes = 'Not again' projector.pjlink_name = 'TEST' projector.manufacturer = 'IN YOUR DREAMS' projector.model = 'OpenLP' # THEN: __repr__ should return a proper string self.assertEqual(str(projector), '< Projector(id="0", ip="127.0.0.1", port="4352", pin="None", name="Test One", ' 'location="Somewhere over the rainbow", notes="Not again", pjlink_name="TEST", ' 'manufacturer="IN YOUR DREAMS", model="OpenLP", other="None", sources="None", ' 'source_list="[]") >', 'Projector.__repr__() should have returned a proper representation string') def test_projectorsource_repr(self): """ Test ProjectorSource.__repr__() text """ # GIVEN: test setup projector1 = Projector(**TEST1_DATA) self.projector.add_projector(projector1) item = self.projector.get_projector_by_id(projector1.id) item_id = item.id # WHEN: A source entry is saved for item source = ProjectorSource(projector_id=item_id, code='11', text='First RGB source') self.projector.add_source(source) # THEN: __repr__ should return a proper string self.assertEqual(str(source), '<ProjectorSource(id="1", code="11", text="First RGB source", projector_id="1")>', 'ProjectorSource.__repr__)_ should have returned a proper representation string') def test_get_projector_by_id_none(self): """ Test get_projector_by_id() returns None if no db entry """ # GIVEN: Test object and data projector = self.projector # WHEN: DB search for entry not saved results = projector.get_projector_by_id(dbid=123134556409824506) # THEN: Verify return was None self.assertEqual(results, None, 'Returned results should have equaled None') def test_get_projector_all_none(self): """ Test get_projector_all() with no projectors in db """ # GIVEN: Test object with no data projector = self.projector # WHEN: We retrieve the database entries results = projector.get_projector_all() # THEN: Verify results is None self.assertEqual(results, [], 'Returned results should have returned an empty list') def test_get_projector_all_one(self): """ Test get_projector_all() with one entry in db """ # GIVEN: One entry in database projector = Projector(**TEST1_DATA) self.projector.add_projector(projector) # WHEN: We retrieve the database entries results = self.projector.get_projector_all() # THEN: We should have a list with one entry self.assertEqual(len(results), 1, 'Returned results should have returned a list with one entry') self.assertTrue((projector in results), 'Result should have been equal to TEST1_DATA') def test_get_projector_all_many(self): """ Test get_projector_all() with multiple entries in db """ # GIVEN: multiple entries in database projector_list = [] projector_list.append(Projector(**TEST1_DATA)) projector_list.append(Projector(**TEST2_DATA)) projector_list.append(Projector(**TEST3_DATA)) for projector in projector_list: self.projector.add_projector(projector) # WHEN: We retrieve the database entries results = self.projector.get_projector_all() # THEN: We should have a list with three entries self.assertEqual(len(results), len(projector_list), 'Returned results should have returned a list with three entries') for projector in results: self.assertTrue((projector in projector_list), 'Projector DB entry should have been in expected list') def test_get_projector_by_name_fail(self): """ Test get_projector_by_name() fail """ # GIVEN: Test entry in database self.projector.add_projector(Projector(**TEST1_DATA)) # WHEN: We attempt to get a projector that's not in database results = self.projector.get_projector_by_name(name=TEST2_DATA['name']) # THEN: We should have None self.assertEqual(results, None, 'projector.get_projector_by_name() should have returned None') def test_add_projector_fail(self): """ Test add_projector() fail """ # GIVEN: Test entry in the database ignore_result = self.projector.add_projector(Projector(**TEST1_DATA)) # WHEN: Attempt to add same projector entry results = self.projector.add_projector(Projector(**TEST1_DATA)) # THEN: We should have failed to add new entry self.assertFalse(results, 'add_projector() should have failed') def test_update_projector_default_fail(self): """ Test update_projector() with no options fails """ # GIVEN: projector instance projector = self.projector # WHEN: attempt to update a projector entry with no options results = projector.update_projector() # THEN: We should have failed self.assertFalse(results, 'update_projector(projector=None) should have returned False') def test_update_projector_not_in_db_fail(self): """ Test update_projector() when entry not in database """ # GIVEN: Projector entry in database ignore_result = self.projector.add_projector(Projector(**TEST1_DATA)) projector = Projector(**TEST2_DATA) # WHEN: Attempt to update data with a different ID results = self.projector.update_projector(projector) # THEN: Results should be False self.assertFalse(results, 'update_projector(projector=projector) should have returned False') def test_delete_projector_fail(self): """ Test delete_projector(projector) fails to delete record """ # GIVEN: Test entry in db self.projector.add_projector(Projector(**TEST1_DATA)) # wHEN: Attempting to delete an entry not in the databae results = self.projector.delete_projector(Projector(**TEST2_DATA)) # THEN: Results should be False self.assertFalse(results, 'delete_projector() should have returned False')