class MDIHistory(QWidget, _HalWidgetBase): def __init__(self, parent=None): super(MDIHistory, self).__init__(parent) self.setMinimumSize(QSize(300, 200)) self.setWindowTitle("PyQt5 editor test example") lay = QVBoxLayout() lay.setContentsMargins(0,0,0,0) self.setLayout(lay) self.list = QListView() self.list.setEditTriggers(QListView.NoEditTriggers) self.list.activated.connect(self.activated) self.list.setAlternatingRowColors(True) self.list.selectionChanged = self.selectionChanged self.model = QStandardItemModel(self.list) self.MDILine = MDILine() self.MDILine.soft_keyboard = False self.MDILine.line_up = self.line_up self.MDILine.line_down = self.line_down # add widgets lay.addWidget(self.list) lay.addWidget(self.MDILine) self.reload() def _hal_init(self): STATUS.connect('state-off', lambda w: self.setEnabled(False)) STATUS.connect('state-estop', lambda w: self.setEnabled(False)) STATUS.connect('interp-idle', lambda w: self.setEnabled(STATUS.machine_is_on() and (STATUS.is_all_homed() or INFO.NO_HOME_REQUIRED))) STATUS.connect('interp-run', lambda w: self.setEnabled(not STATUS.is_auto_mode())) STATUS.connect('all-homed', lambda w: self.setEnabled(STATUS.machine_is_on())) def reload(self, w=None ): print 'RELOAD' try: fp = os.path.expanduser(INFO.MDI_HISTORY_PATH) with open(fp,'r') as inputfile: for line in inputfile: line = line.rstrip('\n') item = QStandardItem(line) self.model.appendRow(item) self.list.setModel(self.model) self.list.scrollToBottom() except Exception as e: print e LOG.error('File path is not valid: {}]n,()'.format(fp),e) def line_up(self): print 'up' def line_down(self): print 'down' def selectionChanged(self,old, new): cmd = self.getSelected() self.MDILine.setText(cmd) def getSelected(self): selected_indexes = self.list.selectedIndexes() selected_rows = [item.row() for item in selected_indexes] # iterates each selected row in descending order for selected_row in sorted(selected_rows, reverse=True): text = self.model.item(selected_row).text() return text def activated(self): cmd = self.getSelected() self.MDILine.setText(cmd) self.MDILine.submit() item = QStandardItem(cmd) self.model.appendRow(item) self.list.update() ######################################################################### # This is how designer can interact with our widget properties. # designer will show the pyqtProperty properties in the editor # it will use the get set and reset calls to do those actions ######################################################################### def set_soft_keyboard(self, data): self.MDILine.soft_keyboard = data def get_soft_keyboard(self): return self.MDILine.soft_keyboard def reset_soft_keyboard(self): self.MDILine.soft_keyboard = False # designer will show these properties in this order: soft_keyboard_option = pyqtProperty(bool, get_soft_keyboard, set_soft_keyboard, reset_soft_keyboard)
class MDIHistory(QWidget, _HalWidgetBase): def __init__(self, parent=None): super(MDIHistory, self).__init__(parent) self.setMinimumSize(QSize(300, 200)) self.setWindowTitle("PyQt5 editor test example") lay = QVBoxLayout() lay.setContentsMargins(0, 0, 0, 0) self.setLayout(lay) self.list = QListView() self.list.setEditTriggers(QListView.NoEditTriggers) self.list.activated.connect(self.activated) self.list.setAlternatingRowColors(True) self.list.selectionChanged = self.selectionChanged self.model = QStandardItemModel(self.list) self.MDILine = MDILine() self.MDILine.soft_keyboard = False self.MDILine.line_up = self.line_up self.MDILine.line_down = self.line_down # add widgets lay.addWidget(self.list) lay.addWidget(self.MDILine) self.reload() def _hal_init(self): STATUS.connect('state-off', lambda w: self.setEnabled(False)) STATUS.connect('state-estop', lambda w: self.setEnabled(False)) STATUS.connect( 'interp-idle', lambda w: self.setEnabled(STATUS.machine_is_on( ) and (STATUS.is_all_homed() or INFO.NO_HOME_REQUIRED))) STATUS.connect('interp-run', lambda w: self.setEnabled(not STATUS.is_auto_mode())) STATUS.connect('all-homed', lambda w: self.setEnabled(STATUS.machine_is_on())) def reload(self, w=None): print 'RELOAD' try: fp = os.path.expanduser(INFO.MDI_HISTORY_PATH) with open(fp, 'r') as inputfile: for line in inputfile: line = line.rstrip('\n') item = QStandardItem(line) self.model.appendRow(item) self.list.setModel(self.model) self.list.scrollToBottom() except Exception as e: print e LOG.error('File path is not valid: {}]n,()'.format(fp), e) def line_up(self): print 'up' def line_down(self): print 'down' def selectionChanged(self, old, new): cmd = self.getSelected() self.MDILine.setText(cmd) def getSelected(self): selected_indexes = self.list.selectedIndexes() selected_rows = [item.row() for item in selected_indexes] # iterates each selected row in descending order for selected_row in sorted(selected_rows, reverse=True): text = self.model.item(selected_row).text() return text def activated(self): cmd = self.getSelected() self.MDILine.setText(cmd) self.MDILine.submit() item = QStandardItem(cmd) self.model.appendRow(item) self.list.update() ######################################################################### # This is how designer can interact with our widget properties. # designer will show the pyqtProperty properties in the editor # it will use the get set and reset calls to do those actions ######################################################################### def set_soft_keyboard(self, data): self.MDILine.soft_keyboard = data def get_soft_keyboard(self): return self.MDILine.soft_keyboard def reset_soft_keyboard(self): self.MDILine.soft_keyboard = False # designer will show these properties in this order: soft_keyboard_option = pyqtProperty(bool, get_soft_keyboard, set_soft_keyboard, reset_soft_keyboard)
class Dialog(QWidget): width_changed = pyqtSignal() def __init__(self, ip): super(Dialog, self).__init__() self.ip = ip self.main_layout = QVBoxLayout() self.header_layout = QHBoxLayout() self.send_layout = QHBoxLayout() self.send_input = QTextEdit() self.send_input.setPlaceholderText(variables.PLACEHOLDER_TEXT) self.send_input.setFont(variables.font) self.send_input.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded) self.send_input.textChanged.connect(self.message_resize) self.send_input.installEventFilter(self) self.send_btn = PicButton(QPixmap(variables.SEND_IDLE_IMG), QPixmap(variables.SEND_HOVER_IMG), QPixmap(variables.SEND_PRESS_IMG)) self.back_btn = PicButton(QPixmap(variables.BACK_IDLE_IMG), QPixmap(variables.BACK_HOVER_IMG), QPixmap(variables.BACK_PRESS_IMG)) self.ip_label = QLabel(ip) self.ip_label.setFont(variables.font) self.messages = QListView() delegate = MessageDelegate() self.messages.setItemDelegate(delegate) self.width_changed.connect( lambda: delegate.sizeHintChanged.emit(QModelIndex())) self.model = MessageModel(ip) self.messages.setModel(self.model) self.send_btn.released.connect(self.message_to) self.back_btn.released.connect(self.return_to_menu) self.header_layout.addWidget(self.back_btn, 0, Qt.AlignVCenter | Qt.AlignLeft) self.header_layout.addWidget(self.ip_label, 0, Qt.AlignVCenter | Qt.AlignLeft) self.header_layout.addStretch() self.main_layout.addLayout(self.header_layout) self.main_layout.addWidget(self.messages) self.send_layout.addWidget(self.send_input) self.send_layout.addWidget(self.send_btn, 0, Qt.AlignBottom) self.main_layout.addLayout(self.send_layout) self.setLayout(self.main_layout) self.message_resize() def return_to_menu(self): variables.signals.close_dialog.emit() def message_to(self): msg = self.send_input.toPlainText() if not msg: return id = self.model.add_message(msg, variables.USER_ME) variables.nw.send_message(id, msg, self.ip) variables.signals.message_sent.emit(msg, self.ip) self.messages.scrollToBottom() self.send_input.clear() self.send_input.setFocus() def message_from(self, msg): self.model.add_message(msg, variables.USER_THEM) def undelivered_status(self, id): self.model.setUndelivered(id) def delivered_status(self, id): self.model.setUnread(id) def read_status(self, id): self.model.setRead(id) def message_resize(self): height = self.send_input.document().documentLayout().documentSize( ).height() fm = QFontMetrics(variables.font) space = self.send_input.document().documentMargin() rowNum = int( (height - (2 * self.send_input.document().documentMargin())) / fm.lineSpacing()) if rowNum == 0: rowNum = 1 if rowNum > variables.MAX_ROWS: rowNum = variables.MAX_ROWS margins = self.send_input.contentsMargins() height = fm.lineSpacing() * rowNum + ( self.send_input.document().documentMargin() + self.send_input. frameWidth()) * 2 + margins.top() + margins.bottom() self.send_input.setFixedHeight(int(height)) def eventFilter(self, widget, event): if event.type() == QEvent.KeyPress: keyEvent = QKeyEvent(event) if (not (keyEvent.modifiers() & Qt.ShiftModifier)) and ( (keyEvent.key() == Qt.Key_Enter) or (keyEvent.key() == Qt.Key_Return)): self.message_to() return True return QWidget.eventFilter(self, widget, event)
class MDIHistory(QWidget, _HalWidgetBase): def __init__(self, parent=None): super(MDIHistory, self).__init__(parent) self.setMinimumSize(QSize(200, 150)) self.setWindowTitle("PyQt5 editor test example") lay = QVBoxLayout() lay.setContentsMargins(0, 0, 0, 0) self.setLayout(lay) self.list = QListView() self.list.setEditTriggers(QListView.NoEditTriggers) self.list.activated.connect(self.activated) self.list.setAlternatingRowColors(True) self.list.selectionChanged = self.selectionChanged self.model = QStandardItemModel(self.list) self.MDILine = MDILine() self.MDILine.soft_keyboard = False self.MDILine.line_up = self.line_up self.MDILine.line_down = self.line_down STATUS.connect('mdi-history-changed', self.reload) # add widgets lay.addWidget(self.list) lay.addWidget(self.MDILine) self.fp = os.path.expanduser(INFO.MDI_HISTORY_PATH) try: open(self.fp, 'r') except: open(self.fp, 'a+') LOG.debug('MDI History file created: {}'.format(self.fp)) self.reload() self.select_row('last') def _hal_init(self): STATUS.connect('state-off', lambda w: self.setEnabled(False)) STATUS.connect('state-estop', lambda w: self.setEnabled(False)) STATUS.connect( 'interp-idle', lambda w: self.setEnabled(STATUS.machine_is_on( ) and (STATUS.is_all_homed() or INFO.NO_HOME_REQUIRED))) STATUS.connect('interp-run', lambda w: self.setEnabled(not STATUS.is_auto_mode())) STATUS.connect('all-homed', lambda w: self.setEnabled(STATUS.machine_is_on())) def reload(self, w=None): self.model.clear() try: with open(self.fp, 'r') as inputfile: for line in inputfile: line = line.rstrip('\n') item = QStandardItem(line) self.model.appendRow(item) self.list.setModel(self.model) self.list.scrollToBottom() if self.MDILine.hasFocus(): self.select_row('last') except: LOG.debug('File path is not valid: {}'.format(fp)) def selectionChanged(self, old, new): cmd = self.getSelected() self.MDILine.setText(cmd) selectionModel = self.list.selectionModel() if selectionModel.hasSelection(): self.row = selectionModel.currentIndex().row() def getSelected(self): selected_indexes = self.list.selectedIndexes() selected_rows = [item.row() for item in selected_indexes] # iterates each selected row in descending order for selected_row in sorted(selected_rows, reverse=True): text = self.model.item(selected_row).text() return text def activated(self): cmd = self.getSelected() self.MDILine.setText(cmd) self.MDILine.submit() self.select_row('down') def run_command(self): self.MDILine.submit() self.select_row('last') def select_row(self, style): style = style.lower() selectionModel = self.list.selectionModel() parent = QModelIndex() self.rows = self.model.rowCount(parent) - 1 if style == 'last': self.row = self.rows print 'last =', self.row elif style == 'up': if self.row > 0: self.row -= 1 else: self.row = 0 elif style == 'down': if self.row < self.rows: self.row += 1 else: self.row = self.rows else: return top = self.model.index(self.row, 0, parent) bottom = self.model.index(self.row, 0, parent) selectionModel.setCurrentIndex( top, QItemSelectionModel.Select | QItemSelectionModel.Rows) selection = QItemSelection(top, top) selectionModel.clearSelection() selectionModel.select(selection, QItemSelectionModel.Select) def line_up(self): self.select_row('up') def line_down(self): self.select_row('down') ######################################################################### # This is how designer can interact with our widget properties. # designer will show the pyqtProperty properties in the editor # it will use the get set and reset calls to do those actions ######################################################################### def set_soft_keyboard(self, data): self.MDILine.soft_keyboard = data def get_soft_keyboard(self): return self.MDILine.soft_keyboard def reset_soft_keyboard(self): self.MDILine.soft_keyboard = False # designer will show these properties in this order: soft_keyboard_option = pyqtProperty(bool, get_soft_keyboard, set_soft_keyboard, reset_soft_keyboard)
class MDIHistory(QWidget, _HalWidgetBase): def __init__(self, parent=None): super(MDIHistory, self).__init__(parent) self.setMinimumSize(QSize(200, 150)) self.setWindowTitle("PyQt5 editor test example") lay = QVBoxLayout() lay.setContentsMargins(0,0,0,0) self.setLayout(lay) self.list = QListView() self.list.setEditTriggers(QListView.NoEditTriggers) self.list.activated.connect(self.activated) self.list.setAlternatingRowColors(True) self.list.selectionChanged = self.selectionChanged self.model = QStandardItemModel(self.list) self.MDILine = MDILine() self.MDILine.soft_keyboard = False self.MDILine.line_up = self.line_up self.MDILine.line_down = self.line_down STATUS.connect('reload-mdi-history', self.reload) # add widgets lay.addWidget(self.list) lay.addWidget(self.MDILine) self.fp = os.path.expanduser(INFO.MDI_HISTORY_PATH) try: open(self.fp, 'r') except: open(self.fp, 'a+') LOG.debug('MDI History file created: {}'.format(self.fp)) self.reload() self.select_row('last') def _hal_init(self): STATUS.connect('state-off', lambda w: self.setEnabled(False)) STATUS.connect('state-estop', lambda w: self.setEnabled(False)) STATUS.connect('interp-idle', lambda w: self.setEnabled(STATUS.machine_is_on() and (STATUS.is_all_homed() or INFO.NO_HOME_REQUIRED))) STATUS.connect('interp-run', lambda w: self.setEnabled(not STATUS.is_auto_mode())) STATUS.connect('all-homed', lambda w: self.setEnabled(STATUS.machine_is_on())) def reload(self, w=None ): self.model.clear() try: with open(self.fp,'r') as inputfile: for line in inputfile: line = line.rstrip('\n') item = QStandardItem(line) self.model.appendRow(item) self.list.setModel(self.model) self.list.scrollToBottom() if self.MDILine.hasFocus(): self.select_row('last') except: LOG.debug('File path is not valid: {}'.format(fp)) def selectionChanged(self,old, new): cmd = self.getSelected() self.MDILine.setText(cmd) selectionModel = self.list.selectionModel() if selectionModel.hasSelection(): self.row = selectionModel.currentIndex().row() def getSelected(self): selected_indexes = self.list.selectedIndexes() selected_rows = [item.row() for item in selected_indexes] # iterates each selected row in descending order for selected_row in sorted(selected_rows, reverse=True): text = self.model.item(selected_row).text() return text def activated(self): cmd = self.getSelected() self.MDILine.setText(cmd) self.MDILine.submit() self.select_row('down') def select_row(self, style): selectionModel = self.list.selectionModel() parent = QModelIndex() self.rows = self.model.rowCount(parent) - 1 if style == 'last': self.row = self.rows elif style == 'up': if self.row > 0: self.row -= 1 else: self.row = self.rows elif style == 'down': if self.row < self.rows: self.row += 1 else: self.row = 0 else: return top = self.model.index(self.row, 0, parent) bottom = self.model.index(self.row, 0, parent) selectionModel.setCurrentIndex(top, QItemSelectionModel.Select | QItemSelectionModel.Rows) selection = QItemSelection(top, top) selectionModel.clearSelection() selectionModel.select(selection, QItemSelectionModel.Select) def line_up(self): self.select_row('up') def line_down(self): self.select_row('down') ######################################################################### # This is how designer can interact with our widget properties. # designer will show the pyqtProperty properties in the editor # it will use the get set and reset calls to do those actions ######################################################################### def set_soft_keyboard(self, data): self.MDILine.soft_keyboard = data def get_soft_keyboard(self): return self.MDILine.soft_keyboard def reset_soft_keyboard(self): self.MDILine.soft_keyboard = False # designer will show these properties in this order: soft_keyboard_option = pyqtProperty(bool, get_soft_keyboard, set_soft_keyboard, reset_soft_keyboard)
class ConversationView(QFrame): """ View Displays an active conversation, including all previous messages and a text field for sending new messages to the recipient """ def __init__(self, parent: Optional[QWidget], client: Client, peer: Peer, friends_list: PeerList, conversation_list: PeerList): super().__init__(parent) self.setObjectName("conversation_view") self._peer = peer self._client = client self._friends_list = friends_list self._conversation_list = conversation_list self._conversation_model = self._client.conversation(self._peer) # Model containing messages self._layout_manager = QVBoxLayout(self) # Configure message list self._message_list = QListView() # View used to display conversation messages (the model) self._message_list.setWordWrap(True) self._message_list.setModel(self._conversation_model) # Set up custom delegate message_delegate = MessageItemDelegate(self._message_list) self._message_list.setItemDelegate(message_delegate) self._send_view = MessageSendView(self, self._conversation_model.peer().username() if self._conversation_model else None) self.setup_ui() def setup_ui(self): """ Builds UI for display """ self._layout_manager.setContentsMargins(0, 0, 0, 0) self._send_view.setContentsMargins(0, 0, 0, 0) self._message_list.setModel(self._conversation_model) # Connect model self._message_list.setLayoutMode(QListView.Batched) # Display as needed self._message_list.setBatchSize(10) # Number of messages to display self._message_list.setFlow(QListView.TopToBottom) # Display vertically self._message_list.setResizeMode(QListView.Adjust) # Items laid out every time view is resized # Layout widgets and views # self._layout_manager.addWidget(header) self._layout_manager.addWidget(self._message_list, 1) self._layout_manager.addWidget(self._send_view) # Connect to signals self._send_view.text_edit().keyPressEvent = self.send_view_did_change self._message_list.verticalScrollBar().rangeChanged.connect(self.scroll_to_message) # Listeners def send_view_did_change(self, event: QKeyEvent): """ As QPlainTextEdit doesn't natively support enter key response, this function prevents the user from typing the 'enter' key and sends a message on its press instead :param event: QKeyEvent raised by key press """ text_field = self._send_view.text_edit() if event.key() == Qt.Key_Return: message: str = text_field.toPlainText() if not message: # Don't want to allow sending of empty messages return # Clear message send view self._send_view.text_edit().clear() # Create message chat_msg = ChatMessage(message) # Send over network to peer self._client.send_chat(self._peer, chat_msg) else: QPlainTextEdit.keyPressEvent(text_field, event) def peer(self): """ Getter :return: this conversation's peer """ return self._peer @QtCore.pyqtSlot() def scroll_to_message(self): """ Event Listener connected to QListView's vertical scroll bar's range change Scrolls to a new message when view reflects a new message in the model """ self._message_list.scrollToBottom()
class AppWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.setGeometry(100, 100, 550, 400) self.setUpMenu() self.setUpLayout() self.checkBoxConnect.stateChanged.connect(self.connect_disconnect) self.addButton.clicked.connect(self.add_contact) self.deleteButton.clicked.connect(self.delete_contact) self.sendButton.clicked.connect(self.send_message) self.contactsList.clicked.connect(self.select_contact) # self.messageField.drop.connect(self.add_file_to_message) # model -controller - view self.contact_list_model = StandardItemModelContacts(self) self.messages_list_model = StandardItemModelMessages(self) self.model = ClientModel(self.contact_list_model, self.messages_list_model) self.contactsList.setModel(self.contact_list_model) self.messagesList.setModel(self.messages_list_model) self.controller = ClientController(self.model) # signal-slot self.controller.auth_recieved.connect(self.authentification_recieved) self.controller.gotPersonal.connect(self.new_personal_message) # not connected self.connected = False # nobody selected self.currently_selected = '' def setUpMenu(self): fileMenu = self.menuBar().addMenu('File') quitAct = QAction('Quit', self) quitAct.triggered.connect(self.doQuit) fileMenu.addAction(quitAct) sendMenu = self.menuBar().addMenu('Send') sendFile = QAction('Send File', self) sendFile.triggered.connect(self.doSendFile) sendMenu.addAction(sendFile) def setUpLayout(self): hBoxLayout = QHBoxLayout() mainFrame = QFrame() mainFrame.setLayout(hBoxLayout) self.contact = QLineEdit() self.contact.setFont(QFont('Calibri', 14)) self.contact.setPlaceholderText('Add Contact Here') self.contact.setMinimumHeight(30) self.contact.setMaximumHeight(30) self.addButton = QPushButton('+') self.addButton.setMinimumHeight(30) self.addButton.setMaximumHeight(30) self.addButton.setMinimumWidth(30) self.addButton.setMaximumWidth(30) self.deleteButton = QPushButton('-') self.deleteButton.setMinimumHeight(30) self.deleteButton.setMaximumHeight(30) self.deleteButton.setMinimumWidth(30) self.deleteButton.setMaximumWidth(30) self.contactsList = QListView() self.contactsList.setFrameShape(QFrame.Box) spacer = QFrame() spacer.setMaximumWidth(5) self.nameLabel = QLabel('Username') self.nameLabel.setMinimumHeight(30) self.nameLabel.setMaximumHeight(30) self.nameLabel.setFont(QFont('Calibri', 14)) self.checkBoxConnect = QCheckBox() self.checkBoxConnect.setText('Connect') self.checkBoxConnect.setChecked(False) self.checkBoxConnect.setMaximumHeight(30) self.checkBoxConnect.setMinimumHeight(30) self.checkBoxConnect.setMaximumWidth(70) self.checkBoxConnect.setMinimumWidth(70) self.messagesList = QListView() self.messagesList.setIconSize(QSize(30, 30)) self.messagesList.setFrameShape(QFrame.Box) self.messageField = MessageField() self.messageField.setMinimumHeight(50) self.messageField.setMaximumHeight(50) self.sendButton = QPushButton() self.sendButton.setText('>>') self.sendButton.setMinimumHeight(50) self.sendButton.setMaximumHeight(50) self.sendButton.setMinimumWidth(50) self.sendButton.setMaximumWidth(50) contBox = QGridLayout() contBox.addWidget(self.contact, 0, 0) contBox.addWidget(self.addButton, 0, 1) contBox.addWidget(self.deleteButton, 0, 2) contBox.addWidget(self.contactsList, 1, 0, 2, 3) contBox.addWidget(spacer, 0, 3, 3, 1) contBox.addWidget(self.nameLabel, 0, 4) contBox.addWidget(self.checkBoxConnect, 0, 5, 1, 2) contBox.addWidget(self.messagesList, 1, 4, 1, 3) contBox.addWidget(self.messageField, 2, 4, 1, 2) contBox.addWidget(self.sendButton, 2, 6) contBox.setColumnStretch(0, 3) contBox.setColumnStretch(4, 6) groupBoxCont = QFrame() groupBoxCont.setLayout(contBox) self.setCentralWidget(groupBoxCont) def doQuit(self): self.close() def doSendFile(self): filename = QFileDialog.getOpenFileName(self, 'Open file', '/home')[0] self.messageField.file = filename self.messageField.appendPlainText(filename) def connect_disconnect(self): self.checkBoxConnect.setEnabled(False) if self.checkBoxConnect.isChecked(): self.authentification() else: self.user_disconnect() self.checkBoxConnect.setEnabled(True) # Log in dialog def authentification(self): #read config file d = conf.read_conf() #show Authorization dialog auth_dialog = AuthDialog(self.model, d['username'], d['hostname'], d['port'], parent=self) auth_dialog.accepted.connect(self.auth_dialog_accepted) auth_dialog.rejected.connect(self.auth_dialog_rejected) auth_dialog.show() def auth_dialog_accepted(self): self.checkBoxConnect.setEnabled(True) self.controller.run_client() def auth_dialog_rejected(self): self.checkBoxConnect.setChecked(False) self.checkBoxConnect.setEnabled(True) @pyqtSlot(bool) def authentification_recieved(self, ok): if ok: self.connected = True self.checkBoxConnect.setText('Connected') self.nameLabel.setText('{}:'.format(self.model.username)) self.model.contactlist.clear() self.model.personal_messages = [] self.model.server_messages = [] self.model.sent_messages = [] self.model.open_db() self.controller.ask_contact_list() else: self.checkBoxConnect.setChecked(False) def user_disconnect(self): if self.connected: conf.save_conf(username=self.model.username, hostname=self.model.host, port=str(self.model.port)) self.model.clear_data() self.controller.user_disconnect() self.contact.setText('') self.nameLabel.setText('') self.checkBoxConnect.setText('Connect') self.connected = False self.currently_selected = '' @pyqtSlot(str) def get_contact_list(self): pass def add_contact(self): name = self.contact.text() if name != '': self.controller.add_contact(name) self.contact.setText('') def delete_contact(self): name = self.contact.text() if name != '': self.controller.delete_contact(name) self.contact.setText('') @pyqtSlot(QMimeData) def add_file_to_message(self, mime): self.messageField.setPlainText(mime.text()) def send_message(self): sel_list = self.contactsList.selectionModel().selectedIndexes() if len(sel_list) > 0: contact = self.model.contactlist.contacts[sel_list[0].row()] if self.messageField.file != '': self.controller.send_file(contact.name, self.messageField.file) self.messageField.file = '' else: self.controller.personal_message( contact.name, self.messageField.toPlainText()) self.messageField.clear() self.model.update_messages_for_contact(contact.name) else: pass @pyqtSlot(QModelIndex) def select_contact(self, modelindex): item = self.contact_list_model.data(modelindex, role=Qt.DisplayRole) contact_name = self.model.contactlist.contacts_name[modelindex.row()] self.contact.setText(contact_name) self.show_messages_for_contact(contact_name) self.contactsList.setCurrentIndex(modelindex) self.currently_selected = contact_name @pyqtSlot(str) def new_personal_message(self, sender): self.model.new_message_increase(sender, True) # sel_list = self.listViewContacts.selectionModel().selectedIndexes() if sender == self.currently_selected: self.show_messages_for_contact(self.currently_selected) @pyqtSlot(str) def show_messages_for_contact(self, contact_name): self.model.update_messages_for_contact(contact_name) self.model.new_message_increase(contact_name, False) self.messagesList.scrollToBottom()
class MainWindow(QMainWindow): logout_signal = pyqtSignal() send_message_signal = pyqtSignal(str, str) start_chatting_signal = pyqtSignal(str) switch_to_add_contact = pyqtSignal() switch_to_del_contact = pyqtSignal() def __init__(self, client_name): super().__init__() self.setFixedSize(756, 574) self.setWindowTitle(f'Python Messenger ({client_name})') central_widget = QWidget() self.label_contacts = QLabel('Contact list:', central_widget) self.label_contacts.setGeometry(QRect(10, 0, 101, 16)) self.add_contact_btn = QPushButton('Add contact', central_widget) self.add_contact_btn.setGeometry(QRect(10, 450, 121, 31)) self.remove_contact_btn = QPushButton('Remove contact', central_widget) self.remove_contact_btn.setGeometry(QRect(140, 450, 121, 31)) self.label_history = QLabel('Chat room:', central_widget) self.label_history.setGeometry(QRect(300, 0, 391, 21)) self.text_message = QTextEdit(central_widget) self.text_message.setGeometry(QRect(300, 360, 441, 71)) self.label_new_message = QLabel('Enter message here:', central_widget) self.label_new_message.setGeometry(QRect(300, 330, 450, 16)) self.toolbar = self.addToolBar('Formatting') self.char_style_resolver = { 'bold': self.set_text_to_bold, 'italic': self.set_text_to_italic, 'underline': self.set_text_to_underline, } self.font_weight = 50 self.font_italic = False self.font_underline = False self.text_font = QFont() self.text_bold = QAction(QIcon('client/ui/icons/b.jpg'), 'Bold', self) self.text_bold.triggered.connect(lambda: self.set_char_style('bold')) self.text_italic = QAction(QIcon('client/ui/icons/i.jpg'), 'Italic', self) self.text_italic.triggered.connect( lambda: self.set_char_style('italic')) self.text_underline = QAction(QIcon('client/ui/icons/u.jpg'), 'Underline', self) self.text_underline.triggered.connect( lambda: self.set_char_style('underline')) self.toolbar.addActions( [self.text_bold, self.text_italic, self.text_underline]) self.list_contacts = QListView(central_widget) self.list_contacts.setGeometry(QRect(10, 20, 251, 411)) self.contacts_model = QStandardItemModel() self.list_contacts.setModel(self.contacts_model) self.list_messages = QListView(central_widget) self.list_messages.setGeometry(QRect(300, 20, 441, 301)) self.messages_model = QStandardItemModel() self.list_messages.setModel(self.messages_model) self.is_list_messages_disable = False self.send_btn = QPushButton('Send', central_widget) self.send_btn.setGeometry(QRect(610, 450, 131, 31)) self.clear_btn = QPushButton('Clear', central_widget) self.clear_btn.setGeometry(QRect(460, 450, 131, 31)) self.setCentralWidget(central_widget) self.menubar = QMenuBar(self) self.menubar.setGeometry(QRect(0, 0, 756, 21)) self.menu = QMenu('File', self.menubar) self.menu_2 = QMenu('Contacts', self.menubar) self.setMenuBar(self.menubar) self.statusBar = QStatusBar(self) self.setStatusBar(self.statusBar) self.menu_exit = QAction('Exit', self) self.menu_logout = QAction('Logout', self) self.menu.addAction(self.menu_exit) self.menu.addAction(self.menu_logout) self.add_contact_menu = QAction('Add new', self) self.del_contact_menu = QAction('Remove some', self) self.menu_2.addAction(self.add_contact_menu) self.menu_2.addAction(self.del_contact_menu) self.menu_2.addSeparator() self.menubar.addAction(self.menu.menuAction()) self.menubar.addAction(self.menu_2.menuAction()) self.message = QMessageBox() self.menu_exit.triggered.connect(qApp.exit) self.menu_logout.triggered.connect(self.menu_logout_handler) self.send_btn.clicked.connect(self.send_btn_handler) self.add_contact_btn.clicked.connect(self.add_contact_btn_handler) self.add_contact_menu.triggered.connect(self.add_contact_btn_handler) self.remove_contact_btn.clicked.connect(self.del_contact_btn_handler) self.del_contact_menu.triggered.connect(self.del_contact_btn_handler) self.list_contacts.doubleClicked.connect( self.list_contacts_click_handler) def set_elements_disable_status(self, status): if not isinstance(status, bool): raise ValueError( f'Disable status must be bool. Got {type(status)}') self.send_btn.setDisabled(status) self.clear_btn.setDisabled(status) self.text_message.setDisabled(status) self.is_list_messages_disable = status def render_welcome_message(self): self.messages_model.clear() msg = QStandardItem( f'Doubleclick to contact in contact list for start chatting.') msg.setEditable(False) self.messages_model.appendRow(msg) def render_contacts(self, contacts): self.contacts_model.clear() if contacts and isinstance(contacts, list): for contact in contacts: rendered_contact = QStandardItem(contact.friend) rendered_contact.setEditable(False) self.contacts_model.appendRow(rendered_contact) def render_messages(self, friend, client_name, messages): self.messages_model.clear() if messages and isinstance(messages, list): for message in messages: self.render_message(friend, client_name, message) self.list_messages.scrollToBottom() def render_message(self, friend, client_name, message): if not self.is_list_messages_disable: if message.from_client in (friend, client_name): date = message.created.replace(microsecond=0) text = message.text from_client = message.from_client msg = QStandardItem(f'{date}\n{from_client}:\n{text}') msg.setEditable(False) if from_client == client_name: msg.setTextAlignment(Qt.AlignRight) msg.setBackground(QBrush(QColor(240, 240, 240))) self.messages_model.appendRow(msg) self.list_messages.scrollToBottom() def menu_logout_handler(self): self.logout_signal.emit() def send_btn_handler(self): message = self.text_message.toPlainText() to_client_name = self.list_contacts.currentIndex().data() if message and to_client_name: self.send_message_signal.emit(to_client_name, message) self.text_message.clear() def add_contact_btn_handler(self): self.switch_to_add_contact.emit() def del_contact_btn_handler(self): self.switch_to_del_contact.emit() def list_contacts_click_handler(self): self.messages_model.clear() self.set_elements_disable_status(False) friend = self.list_contacts.currentIndex().data() self.start_chatting_signal.emit(friend) def set_char_style(self, style): cursor = self.text_message.textCursor() char_format = QTextCharFormat() self.char_style_resolver[style](char_format) cursor.mergeCharFormat(char_format) def set_text_to_bold(self, char_format): bold_weight = 600 thin_weight = 50 weight = bold_weight if self.font_weight < bold_weight else thin_weight char_format.setFontWeight(weight) self.font_weight = weight def set_text_to_italic(self, char_format): char_format.setFontItalic(not self.font_italic) self.font_italic = not self.font_italic def set_text_to_underline(self, char_format): char_format.setFontUnderline(not self.font_underline) self.font_underline = not self.font_underline