class ViewSourceDialogTabber(QMainWindow): def __init__(self, parent=None, title="Source"): super(ViewSourceDialogTabber, self).__init__(parent) self.setWindowTitle(title) self.tabs = QTabWidget(self) self.tabs.setElideMode(Qt.ElideRight) self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(self.removeTab) self.setCentralWidget(self.tabs) self.tabs.currentChanged.connect(self.updateWindowTitle) def sizeHint(self): return QSize(640, 480) def removeTab(self): self.tabs.removeTab(self.tabs.currentIndex()) def addTab(self, title="New Tab", data=""): vsd = ViewSourceDialog(self, str(title)) vsd.setPlainText(data) self.tabs.addTab(vsd, tr("Source of %s") % (title,)) self.tabs.setCurrentIndex(self.tabs.count()-1) self.raise_() self.activateWindow() def updateWindowTitle(self): try: self.setWindowTitle(tr("Source of %s") % (self.tabs.currentWidget().windowTitle(),)) except: pass if self.tabs.count() == 0: self.hide()
class MyPlugin(Plugin): def __init__(self, context): super(MyPlugin, self).__init__(context) self.setObjectName("main_window") self._widget = QWidget() self._widget.setMinimumSize(1280, 800) self._widget.setWindowTitle("Yonah RQt") context.add_widget(self._widget) # Get path to UI file and load it ui_file = os.path.join(rospkg.RosPack().get_path("yonah_rqt"), "resource", "second_window.ui") loadUi(ui_file, self._widget) # Show _widget.windowTitle on left-top of each plugin (when it's set in _widget). This is useful when you open multiple # plugins at once. Also if you open multiple instances of your plugin at once, these lines add number to make it easy to # tell from pane to pane. if context.serial_number(): self._widget.setWindowTitle(self._widget.windowTitle() + (" (%d)" % context.serial_number())) # Declare attributes self.saved = "" self.time = 0 self.proper_shutdown = 0 self.destination_id = 1 self.aircraft_list = [] self.aircrafts_info = {} self.checklist_info = {} self.aircrafts_flight_data = {} # Declare attributes for each imported class self.PopupMessages = PopupMessages() self.WaypointWindow = WaypointWindow(self.aircraft_list) self.SummaryWindow = SummaryWindow(self.aircraft_list) self.CommandWindow = CommandWindow(self.aircraft_list, self.tab_change) self.CommandWindow.change_valid_ids() self.create_layout() self.shortcuts() # Subscriber lists rospy.Subscriber("ogc/from_despatcher/regular", RegularPayload, self.regular_payload) rospy.Subscriber("ogc/yonahtext", String, self.status_text) rospy.Subscriber("ogc/from_despatcher/ondemand", String, self.ondemand) rospy.Subscriber("ogc/files/conflict", String, self.syncthing) rospy.Subscriber("ogc/feedback_to_rqt", LinkMessage, self.feedback_message) rospy.Subscriber("mavros/statustext/recv", StatusText, self.ondemand_sitl) rospy.Subscriber("mavros/state", State, self.mode_status_sitl) rospy.Subscriber("mavros/vfr_hud", VFR_HUD, self.VFR_HUD_sitl) rospy.Subscriber("mavros/mission/waypoints", WaypointList, self.waypoint_sitl) # Publisher List self.command_publisher = rospy.Publisher("ogc/to_despatcher", LinkMessage, queue_size=5) def create_layout(self): '''Populate the UI with the modules from other files''' # Create layout for Waypoint scroll window scroll = QScrollArea() scroll.setMinimumHeight(800) scroll.setMinimumWidth(600) scroll.setWidgetResizable(True) scroll.setWidget(self.WaypointWindow) # Create the tab windows for the aircraft-specific information self.create_tab_windows([]) self.tab.currentChanged.connect(self.tab_change) # Add all 3 layouts into the main layout self._widget.verticalLayout.addWidget(scroll) self._widget.verticalLayout2.addWidget(self.tab) self._widget.verticalLayout2.addWidget(self.CommandWindow) def create_tab_windows(self, active_aircrafts): '''Create layout for Summary scroll window''' if active_aircrafts == []: self.tab = QTabWidget() summary_scroll = QScrollArea() summary_scroll.setMinimumHeight(500) summary_scroll.setMinimumWidth(600) summary_scroll.setWidgetResizable(True) summary_scroll.setWidget(self.SummaryWindow) self.tab.addTab(summary_scroll, "Summary") self.SummaryWindow.create_layout(active_aircrafts) self.CommandWindow.create_combobox( self.aircraft_list ) #for checkbox, it needs to full list and not just the added aircraft self.WaypointWindow.create_layout(active_aircrafts) for i in active_aircrafts: self.aircrafts_info["AC" + str(i)] = AircraftInfo(i) self.checklist_info["AC" + str(i)] = ChecklistWindow(i) tab_key = "aircraft" + str(i) + " scroll" self.aircrafts_info[tab_key] = QScrollArea() self.aircrafts_info.get(tab_key).setMinimumHeight(500) self.aircrafts_info.get(tab_key).setMinimumWidth(600) self.aircrafts_info.get(tab_key).setWidgetResizable(True) self.aircrafts_info.get(tab_key).setWidget( self.aircrafts_info.get("AC" + str(i))) self.tab.addTab(self.aircrafts_info.get(tab_key), "Aircraft " + str(i)) self.tab.show() self.tab.setMinimumHeight(500) def tab_change(self, i): '''Changes the command_window drop-down menu to follow the change in tab''' active_aircrafts = self.CommandWindow.ValidIdWindow.valid_ids diff_active = list(set(active_aircrafts) - set(self.aircraft_list)) if not diff_active == []: self.aircraft_list = active_aircrafts self.create_tab_windows(diff_active) for i in range(self.tab.count()): if self.tab.tabText(i) != "Summary": aircraft_no = int(self.tab.tabText(i)[-1]) if aircraft_no < i: self.tab.tabBar().moveTab(i, aircraft_no) command_window_index = self.tab.currentIndex() - 1 if command_window_index < 0: command_window_index = 0 # If tab is at Summary page, set Combobox to 0th index self.CommandWindow.combo_box.setCurrentIndex(command_window_index) def feedback_message(self, data): status = Communicate() status.feedback_message_signal.connect(self.feedback_message_display) status.feedback_message_signal.emit(data.data) def feedback_message_display(self, data): self.PopupMessages.warning_message("Command failed to send", data) ################################ # Create Signal Slot functions # ################################ def regular_payload(self, data): aircraft_id = str(data.vehicle_no) status = Communicate() status.airspeed_signal.connect(self.airspeed_display) status.airspeed_signal.emit(data.airspeed, aircraft_id) status.alt_signal.connect(self.altitude_display) status.alt_signal.emit(data.alt, aircraft_id) status.arm_signal.connect(self.arm_status_display) status.arm_signal.emit(data.armed, aircraft_id) status.batt_signal.connect(self.quad_batt_display) status.batt_signal.emit(data.batt, aircraft_id) status.fuel_signal.connect(self.fuel_display) status.fuel_signal.emit(data.fuel, aircraft_id) status.groundspeed_signal.connect(self.groundspeed_display) status.groundspeed_signal.emit(data.groundspeed, aircraft_id) status.gps_signal.connect(self.gps_display) status.gps_signal.emit(data.lat, data.lon, aircraft_id) status.mode_signal.connect(self.mode_status_display) status.mode_signal.emit(data.mode, aircraft_id) status.throttle_signal.connect(self.throttle_display) status.throttle_signal.emit(data.throttle, aircraft_id) status.vibe_signal.connect(self.vibe_display) status.vibe_signal.emit(data.vibe, aircraft_id) status.vtol_signal.connect(self.vtol_display) status.vtol_signal.emit(data.vtol, aircraft_id) status.wp_signal.connect(self.waypoint_display) status.wp_signal.emit(data.wp, data.wp_total, aircraft_id) status.time_signal.connect(self.time_display) status.time_signal.emit(data.header.stamp.secs, aircraft_id) def ondemand(self, data): # data. data list is i 1 1 0 message data_list = data.data.split() msg = " ".join(data_list[4:-1]) aircraft_id = data_list[2] status = Communicate() if "LinkSwitch" in msg: status.ondemand_signal.connect(self.link_status) else: status.ondemand_signal.connect(self.ondemand_display) status.ondemand_signal.emit( msg, aircraft_id) # Change the id where to display using headers module def ondemand_sitl(self, data): status = Communicate() status.ondemand_signal.connect(self.ondemand_display) status.ondemand_signal.emit(data.text, "1") def status_text(self, data): status = Communicate() status.status_text_signal.connect(self.status_text_display) status.status_text_signal.emit(data.data) def syncthing(self, data): status = Communicate() status.syncthing_signal.connect(self.syncthing_conflict) status.syncthing_signal.emit(data) ####### MAVROS Signal-Slot Functions ####### def mode_status_sitl(self, data): status = Communicate() status.mode_sitl_signal.connect(self.mode_status_display_sitl) status.mode_sitl_signal.emit(data.mode, "1") status.arm_signal.connect(self.arm_status_display) status.arm_signal.emit(data.armed, "1") def VFR_HUD_sitl(self, data): status = Communicate() status.airspeed_signal.connect(self.airspeed_display) status.airspeed_signal.emit(data.airspeed, "1") status.alt_signal.connect(self.altitude_display) status.alt_signal.emit(data.altitude, "1") def waypoint_sitl(self, data): status = Communicate() status.waypoint_list_signal.connect(self.waypoint_sitl_display) status.waypoint_list_signal.emit(data.waypoints, data.current_seq, "1") ################################################## # Display information from Signal Slot functions # ################################################## def airspeed_display(self, airspeed, aircraft_id): self.aircrafts_flight_data['airspeed' + aircraft_id] = airspeed airspeed = str(round(airspeed, 1)) + " m/s" self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftAirspeed" + aircraft_id).setStyleSheet("Color: rgb(255, 0, 0);") self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftAirspeed" + aircraft_id).setPlainText(airspeed) self.SummaryWindow.summary_plaintext_dict.get( "aircraftAirspeed" + aircraft_id).setPlainText(airspeed) def altitude_display(self, altitude, aircraft_id): self.aircrafts_flight_data['altitude' + aircraft_id] = altitude altitude = str(round(altitude, 1)) + " m" self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftAltitude" + aircraft_id).setPlainText(altitude) self.SummaryWindow.summary_plaintext_dict.get( "aircraftAltitude" + aircraft_id).setPlainText(altitude) def arm_status_display(self, arm_status, aircraft_id): self.aircrafts_flight_data['status' + aircraft_id] = arm_status if arm_status == "False" or arm_status == 0: self.text_to_display = "DISARMED" else: if self.CommandWindow.arm_status.get('AC' + str( aircraft_id)) == None or self.CommandWindow.arm_status.get( 'AC' + str(aircraft_id)) == "DISARMED": self.aircrafts_info.get("AC" + aircraft_id).initial_time = self.time self.text_to_display = "ARMED" self.CommandWindow.arm_status['AC' + str(aircraft_id)] = self.text_to_display self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftStatus" + aircraft_id).setPlainText(self.text_to_display) self.SummaryWindow.summary_plaintext_dict.get( "aircraftStatus" + aircraft_id).setPlainText(self.text_to_display) def quad_batt_display(self, data, aircraft_id): self.aircrafts_flight_data['battery' + aircraft_id] = data data = str(data) self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftQuad Battery" + aircraft_id).setPlainText(data) def fuel_display(self, data, aircraft_id): self.aircrafts_flight_data['fuel' + aircraft_id] = data data = str(data) self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftFuel Level" + aircraft_id).setPlainText(data) def groundspeed_display(self, gndspeed, aircraft_id): self.aircrafts_flight_data['groundspeed' + aircraft_id] = gndspeed data = str(round(gndspeed, 1)) + " m/s" self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftGroundspeed" + aircraft_id).setPlainText(data) def gps_display(self, lat, lon, aircraft_id): data = [lat, lon] self.aircrafts_flight_data['gps' + aircraft_id] = data self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftGPS" + aircraft_id).setPlainText(str(lat) + ", " + str(lon)) def mode_status_display(self, mode_status, aircraft_id): self.aircrafts_flight_data['mode' + aircraft_id] = mode_status mode = self.CommandWindow.decoder[ mode_status] # Convert the integer to its mode self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftMode" + aircraft_id).setPlainText(mode) self.SummaryWindow.summary_plaintext_dict.get( "aircraftMode" + aircraft_id).setPlainText(mode) def throttle_display(self, data, aircraft_id): self.aircrafts_flight_data['throttle' + aircraft_id] = data data = str(data) self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftThrottle" + aircraft_id).setPlainText(data) def vibe_display(self, data, aircraft_id): self.aircrafts_flight_data['vibe' + aircraft_id] = data data = str(data) self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftVibe Status" + aircraft_id).setPlainText(data) def vtol_display(self, data, aircraft_id): self.aircrafts_flight_data['vtol' + aircraft_id] = data data = str(data) self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftVTOL Status" + aircraft_id).setPlainText(data) def waypoint_display(self, waypoint, total_waypoint, aircraft_id): self.aircrafts_flight_data['waypoint' + aircraft_id] = (waypoint, total_waypoint) self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftTarget Waypoint" + aircraft_id).setPlainText( str(waypoint)) self.WaypointWindow.waypoint_plaintext_dict.get( "progress_bar_aircraft" + aircraft_id).setRange(0, total_waypoint) self.WaypointWindow.waypoint_plaintext_dict.get( "progress_bar_aircraft" + aircraft_id).setValue(waypoint) self.WaypointWindow.waypoint_plaintext_dict.get( "aircraft" + aircraft_id).setPlainText("Current WP: " + str(waypoint) + " out of " + str(total_waypoint)) def time_display(self, AC_time, aircraft_id): if self.text_to_display == "DISARMED": self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftFlying Time" + aircraft_id).setPlainText("00:00:00") else: self.time = AC_time # sync the UI time to the data time time_in_seconds = int(self.time) - int( self.aircrafts_info.get("AC" + aircraft_id).initial_time) minutes = str(time_in_seconds // 60) hours = str(time_in_seconds // 3600) seconds = str(time_in_seconds - (int(minutes) * 60) - (int(hours) * 3600)) if int(seconds) < 10: seconds = "0" + seconds if int(minutes) < 10: minutes = "0" + minutes if int(hours) < 10: hours = "0" + hours self.aircrafts_flight_data['time' + aircraft_id] = self.aircrafts_info.get( "AC" + aircraft_id).initial_time self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftFlying Time" + aircraft_id).setPlainText(hours + ":" + minutes + ":" + seconds) def link_status(self, link, aircraft_id): link = link[-1] # extract the status if int(link) == 0: link = "Telegram" elif int(link) == 1: link = "SMS" elif int(link) == 2: link = "SBD" else: link = "ERR" self.WaypointWindow.waypoint_plaintext_dict.get( "aircraftlink" + aircraft_id).setPlainText(link) self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftLink Status" + aircraft_id).setPlainText(link) def status_text_display(self, status_text): status = status_text.split(",") # time_stamp = int(status[-1]) # Timestamp not needed message_type = status[0] aircraft_id = status[2] info = status[4:-1] display_text = "Aircraft {} : {}".format(aircraft_id, str(info[0])) self.SummaryWindow.statustext.appendPlainText(display_text) self.aircrafts_info.get("AC" + aircraft_id).statustext.appendPlainText( display_text) def ondemand_display(self, data, aircraft_id): status = "" text_to_display = "Aircraft {} {}: {}".format(aircraft_id, status, data) self.SummaryWindow.statustext.appendPlainText(text_to_display) self.aircrafts_info.get("AC" + aircraft_id).statustext.appendPlainText( text_to_display) ####### SITL Display Functions (for developer testing only) ####### def mode_status_display_sitl(self, mode_status, aircraft_id): self.aircrafts_flight_data['mode_sitl' + aircraft_id] = mode_status mode_status = str(mode_status) self.aircrafts_info.get("AC" + aircraft_id).aircraft_info_dict.get( "aircraftMode" + aircraft_id).setPlainText(mode_status) self.SummaryWindow.summary_plaintext_dict.get( "aircraftMode" + aircraft_id).setPlainText(mode_status) self.saved = "[AC {} MODE display] {}".format(int(aircraft_id), mode_status) def waypoint_sitl_display(self, total, sequence, aircraft_id): self.aircrafts_flight_data['waypoint_sitl' + aircraft_id] = [total, sequence] total = len(total) - 1 self.WaypointWindow.waypoint_plaintext_dict.get( "progress_bar_aircraft" + aircraft_id).setRange(0, total) self.WaypointWindow.waypoint_plaintext_dict.get( "progress_bar_aircraft" + aircraft_id).setValue(sequence) self.WaypointWindow.waypoint_plaintext_dict.get( "aircraft" + aircraft_id).setPlainText("Current WP: " + str(sequence) + " out of " + str(total)) def shortcuts(self): '''Create keyboard short-cuts''' disarming = QShortcut(self._widget) disarming.setKey(Qt.CTRL + Qt.Key_D) disarming.activated.connect(self.PopupMessages.emergency_disarm) shutdown = QShortcut(self._widget) shutdown.setKey(Qt.ALT + Qt.Key_F4) shutdown.activated.connect(self.shutdown_plugin) def syncthing_conflict(self, file_name): heading = "Your edit to the file {} encountered an error".format( file_name) info_text = "This might be caused due edits happening at the same time \n Please check whether there is another person editing the same file" self.PopupMessages.warning_message(heading, info_text) def shutdown_plugin(self): self.proper_shutdown = 1 '''Shutdown function''' # Shutdown all the checklist windows for i in self.aircraft_list: self.checklist_info.get("AC" + str(i)).shutdown() # Shutdown all identifiers windows opened_CommandWindows = [ x for x in self.CommandWindow.windows_opened.keys() if self.CommandWindow.windows_opened.get(x) ] opened_PopupMessages = [ x for x in self.PopupMessages.windows_opened.keys() if self.PopupMessages.windows_opened.get(x) ] opened_windows = opened_CommandWindows + opened_PopupMessages for i in opened_windows: if i == "full_menu": self.CommandWindow.full_widget.close() elif i == "change_identifiers_dialog": self.CommandWindow.change_identifiers_dialog.close() elif i == "add_identifiers_dialog": self.CommandWindow.add_identifiers_dialog.close() elif i == "edit_identifiers_dialog": self.CommandWindow.edit_identifiers_dialog.close() elif i == "arm window" or i == "disarm window": self.CommandWindow.PopupMessages.message.close() elif i == "checklist window": self.CommandWindow.checklist_info.get( "AC" + str(self.destination_id)).close() elif i == "change_valid_ids": self.CommandWindow.ValidIdWindow.close() self.WaypointWindow.shutdown() self.SummaryWindow.shutdown() def save_settings(self, plugin_settings, instance_settings): with open(self.path, 'r') as lines: data = lines.readlines() for i in range(len(data)): data[i] = "None\n" with open(self.path, 'w') as files: files.writelines(data) files.close() def restore_settings(self, plugin_settings, instance_settings): filename = "rqt_log.txt" self.path = os.path.join(rospkg.RosPack().get_path("yonah_rqt"), "src/yonah_rqt", filename) file_exists = os.path.isfile(self.path) if file_exists: with open(self.path, 'r') as lines: data = lines.readlines() for i in data: i = i[:-1] if i == "None": continue else: regpay = i.split(" ") msg = regular.convert_to_rosmsg(regpay) self.regular_payload(msg) else: rospy.loginfo( "rqt: rqt_log file doesn't exist, creating new rqt_log file") f = open(self.path, "w+") f.write("None")
class ProteinQuantification(QMainWindow): """ Application to use different Widgets in one Window: First Tab: Welcome tab with information about how the GUI works. Second Tab: Config view - here the .ini file can be viewed and edited Third Tab: mzMLTable view - the experimental design can be viewed and edited. Fourth Tab: Fasta view - fasta files can be loaded and inspected Fifth Tab: Spec view - ms spectras from loaded mzML files can be seen and inspected Sixth Tab: mzTabTable view - The result of the ProteomicsLFQ is displayed """ def __init__(self): QMainWindow.__init__(self) self.initUI() self.initVars() # flag for themetoggle self.flag = False self.setPalette(self.palette) self.setTheme() self.setAcceptDrops(True) def initUI(self): ''' Sets the window with all applications and widgets. ''' descriptions = desc.descriptions widgetlist = { "Welcome": [QWidget(), "welcome"], "XML-Viewer": [ConfigView(), "cview"], "Experimental-Design": [mzMLTableView(), "tview"], "Fasta-Viewer": [GUI_FastaViewer(), "fview"], "Spec-Viewer": [MultipleSpecView(), "sview"], "mzTab-Viewer": [mzTabTableWidget(), "xview"] } self.view = QTabWidget() for wname in widgetlist: a = widgetlist[wname][0] setattr(self, widgetlist[wname][1], a) self.view.addTab(a, wname) #self.view.setTabEnabled(5, False) self.palette = QPalette() menubar = self.menuBar() menubar.setNativeMenuBar(False) projectMenu = menubar.addMenu('Project') parametersMenu = menubar.addMenu('Parameters') loadAction = QAction(QIcon("Icons/load_icon.png"), "&Load Project", self) loadAction.setShortcut("Ctrl+O") saveAction = QAction(QIcon("Icons/save_icon.png"), "&Save Project", self) saveAction.setShortcut("Ctrl+S") runAction = QAction(QIcon("Icons/run_icon.png"), "&Run in Terminal", self) runAction.setShortcut("Ctrl+R") Threads = QAction("&Adjust the Threadnumber", self) FDR = QAction("&Adjust the protein FDR", self) Out = QAction("&Choose outputfiles", self) projectMenu.addAction(loadAction) projectMenu.addAction(saveAction) projectMenu.addAction(runAction) parametersMenu.addAction(Threads) parametersMenu.addAction(FDR) parametersMenu.addAction(Out) runAction.triggered.connect(self.runFunction) FDR.triggered.connect(self.adjustFDR) Threads.triggered.connect(self.adjustThreads) Out.triggered.connect(self.chooseOutputfiles) saveAction.triggered.connect(self.saveFunction) loadAction.triggered.connect(self.loadFunction) #themeswitcher settingsMenu = menubar.addMenu('Settings') switchThemeAction = QAction('Change Theme', self) settingsMenu.addAction(switchThemeAction) switchThemeAction.triggered.connect(self.switchTheme) # Welcome Tab normalFont = QFont("Helvetica", 11) welcome = QLabel() welcome.setText(descriptions["welcome"]) welcome.setFont(normalFont) welcome_layout = QVBoxLayout() welcome_layout.addWidget(welcome, 2, Qt.AlignTop) iconOpenMs = QPixmap("Icons/IconOpenMS.png") iconLabel = QLabel() iconLabel.setPixmap(iconOpenMs) welcome_layout.addWidget(iconLabel, 4, Qt.AlignTop) center_layout = QVBoxLayout() view = self.view for i in range(1, view.count()): print(view.tabText(i)) label = QLabel() label.setText(descriptions[view.tabText(i)]) label.setFont(normalFont) if i == 1: center_layout.addWidget(label, 4, Qt.AlignTop) else: center_layout.addWidget(label, 4) central_layout = QHBoxLayout() central_layout.addLayout(welcome_layout, 5) central_layout.addLayout(center_layout, 5) self.welcome.setLayout(central_layout) self.setCentralWidget(self.view) self.resize(1280, 720) self.center() self.setWindowTitle('Protein Quantification') self.show() # print(self.view.count()) self.view.currentChanged.connect(self.onChange) def initVars(self): """ initiates usable variables """ self.ini_loaded = False self.tablefile_loaded = False self.fasta_loaded = False self.mztab_loaded = False self.cxml_out = True self.msstats_out = True self.loaded_dir = "" self.loaded_ini = "" self.loaded_table = "" self.loaded_fasta = "" self.loaded_mztab = "" self.threads = 1 self.fdr = 0.3 self.procdone = False def center(self): """ centers the widget to the screen """ qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def chooseOutputfiles(self): """ Opens a popup window to choose the outputfiles If output is generated checkbox is checked. """ # Popup self.outputCheckBoxWindow = PopupWindow() mainWidget = QWidget() mztabCheckbox = QCheckBox("Generate a mzTab outputfile") cxmlCheckbox = QCheckBox("Generate a cxml outputfile") msstatsCheckbox = QCheckBox("Generate a msstats outputfile") layout = QVBoxLayout() layout.addWidget(mztabCheckbox) layout.addWidget(cxmlCheckbox) layout.addWidget(msstatsCheckbox) mainWidget.setLayout(layout) self.outputCheckBoxWindow.setCentralWidget(mainWidget) self.outputCheckBoxWindow.setTitle("Choose Outputfiles") # Checkboxstates mztabCheckbox.setChecked(True) mztabCheckbox.setEnabled(False) if self.cxml_out: cxmlCheckbox.setChecked(True) if self.msstats_out: msstatsCheckbox.setChecked(True) # Change Checkbox cxmlCheckbox.clicked.connect(self.togglecxml) msstatsCheckbox.clicked.connect(self.togglemsstats) def togglecxml(self): if self.cxml_out: self.cxml_out = False else: self.cxml_out = True def togglemsstats(self): if self.msstats_out: self.msstats_out = False else: self.msstats_out = True def adjustFDR(self): """ The user is allowed to change th FDR """ newfdr, ok = QInputDialog.getDouble( self, "Adjust the value for the protein FDR", "Please specify a double as new FDR value") if ok: if newfdr > 0: if newfdr <= 1: self.fdr = newfdr else: QMessageBox.about( self, "Warning", "Please specify a value" + "between 0.0 and 1.0 for the FDR.") else: QMessageBox.about( self, "Warning", "Please specify a positive" + "value for the FDR.") def adjustThreads(self): """ The user is allowed to change the number of threads """ newthreads, ok = QInputDialog.getInt( self, "Adjust the number of threads", "Please specify the number of threads for the processing") if ok: if newthreads > 0: self.threads = newthreads else: QMessageBox.about( self, "Warning", "Please specify a positive" + "number of threads.") def runFunction(self): """ runs the processing from the GUI in a Terminal based on the ProteomicsLFQ command of OpenMS """ self.view.setTabEnabled(5, True) #self.saveFunction() dlg = QFileDialog(self) filePath = dlg.getExistingDirectory() self.procdone = False outfileprefix, ok = QInputDialog.getText( self, "Prefix for outputfiles", "Please specify a prefix " + "for the outputfiles") outfileprefix = filePath + '/' + outfileprefix if ok: projectfolder = self.loaded_dir mzMLExpLayout = self.tview.getDataFrame() try: mzMLfiles = mzMLExpLayout['Spectra_Filepath'] idXMLfiles = [] for mzML in mzMLfiles: temp = mzML.split(".") idXML = temp[0] + ".idXML" idXMLfiles.append(idXML) mzMLidXMLdefined = True except KeyError: QMessageBox.about( self, "Warning", "Please load or " + "create an Experimental Design first") mzMLidXMLdefined = False expdesign = self.loaded_table dbfasta = self.loaded_fasta inifile = self.loaded_ini if mzMLidXMLdefined: runcall = "ProteomicsLFQ " mzMLs = "-in " + " ".join(mzMLfiles) idXMLs = " -ids " + " ".join(idXMLfiles) design = " -design " + expdesign + " " refdb = "-fasta " + dbfasta + " " configini = "-ini " + inifile + " " threads = "-threads " + str(self.threads) + " " fdr = "-proteinFDR " + str(self.fdr) + " " out = "" if self.cxml_out: out += "-out_cxml " + outfileprefix + ".consensusXML.tmp " if self.msstats_out: out += "-out_msstats " + outfileprefix + ".csv.tmp " out += "-out " + outfileprefix + ".mzTab.tmp" command = (runcall + mzMLs + idXMLs + design + refdb + configini + threads + fdr + out) os.chdir(projectfolder) os.system(command) self.procdone = True QMessageBox.about( self, "Information", "Processing has been " + "performed and outputfiles saved to " + "projectfolder") mztabfile = outfileprefix + ".mzTab.tmp" #print(mztabfile) try: self.xview.readFile(mztabfile) self.loaded_mztab = mztabfile self.mztab_loaded = True self.view.setCurrentWidget(self.xview) except FileNotFoundError: QMessageBox.about( self, "Warning", "Some Error occurred " + "and no mzTab could be found.") def saveFunction(self): """ saves all work from the GUI in chosen folder the prefix of the outputfiles can be choosen """ dlg = QFileDialog(self) filePath = dlg.getExistingDirectory() if filePath: filePath = filePath + "/" tablePath = "" ok = False table_empty = self.tview.table.rowCount() <= 0 if table_empty: tablePath = "" self.tablefile_loaded = False self.tview.tablefile_loaded = False if self.tablefile_loaded is False and table_empty is False \ and self.tview.tablefile_loaded is False: prefix, ok = QInputDialog.getText( self, "Prefix for outputfiles", "Please specify a prefix " + "for the outputfiles") if ok: tablePath = filePath + prefix + "_design.tsv" self.loaded_table = prefix + "_design.tsv" self.tablefile_loaded = True if self.tablefile_loaded and self.tview.tablefile_loaded is False: tablePath = filePath + self.loaded_table if self.tview.tablefile_loaded: tablePath = filePath + self.tview.loaded_table if (ok or self.tablefile_loaded or self.tview.tablefile_loaded) \ and table_empty is False: df = Tdf.getTable(self.tview) fh.exportTable(self.tview, df, tablePath, "tsv") xmlPath = filePath + self.loaded_ini try: self.cview.tree.write(xmlPath) except TypeError: print("No Config loaded to be saved!") if self.loaded_ini != "" and tablePath.split("/")[-1] != "": QMessageBox.about( self, "Successfully saved!", "Files have been saved as: " + self.loaded_ini + ", " + tablePath.split("/")[-1]) elif self.loaded_ini != "": QMessageBox.about(self, "Successfully saved!", "ini has been saved as: " + self.loaded_ini) elif tablePath.split("/")[-1] != "": QMessageBox.about( self, "Successfully saved!", "Table has been saved as: " + tablePath.split("/")[-1]) def loadFunction(self, filePath: str = ""): """ loads all files (.tsv .ini, .fasta) from a given directory. If .tsv file is not present the experimental design is filled with mzMl files If no .ini file is present default ini file is written and is loaded automatically """ if not filePath: dlg = QFileDialog(self) filePath = dlg.getExistingDirectory() self.loaded_dir = filePath self.sview.fillTable(filePath) if filePath: try: self.tsvfiles = glob.glob('*.tsv') if len(self.tsvfiles) > 1: QMessageBox.about( self, "Sorry!", "There are multiple '.tsv-'" "files in the specified folder. " "Please choose the one you intent " "to use.") dial = QFileDialog(self) newFilePath = dial.getOpenFileName(self, "Choose .tsv", filePath, "Tables (*.tsv)") if newFilePath[0] != '': newFile = newFilePath[0].split("/")[-1] self.tsvfiles = [newFile] else: QMessageBox.about( self, "Sorry!", "Nothing was choosen. " "Therefore no '.tsv'-file was " "loaded. ") self.tsvfiles = [] for file in self.tsvfiles: df = fh.importTable(self.tview, file) Tdf.setTable(self.tview, df) self.tview.drawTable() self.loaded_table = file self.tablefile_loaded = True except TypeError: "No '.tsv' or '.csv'-file could be loaded." if self.tablefile_loaded is False: try: self.tview.loadDir(filePath) self.tablefile_loaded = True except TypeError: print("Could not load '.mzMl'-files.") try: self.iniFiles = glob.glob('*.ini') if len(self.iniFiles) > 1: QMessageBox.about( self, "Sorry!", "There are multiple '.ini'-" "files in the specified folder. " "Please choose the one you intent " "to use.") dial = QFileDialog(self) newFilePath = dial.getOpenFileName(self, "Choose .ini", filePath, "Config (*.ini)") if newFilePath[0] != '': newFile = newFilePath[0].split("/")[-1] self.iniFiles = [newFile] else: QMessageBox.about( self, "Sorry!", "Nothing was choosen. " "Therefore no '.ini'-file was " "loaded. ") self.iniFiles = [] for file in self.iniFiles: self.cview.generateTreeModel(file) self.loaded_ini = file self.ini_loaded = True except TypeError: print("Could not load .ini file.") if self.ini_loaded is False: try: runcall = "ProteomicsLFQ " writeIniFile = "-write_ini " out = "Config.ini" command = (runcall + writeIniFile + out) os.chdir(filePath) os.system(command) iniFiles = glob.glob('*.ini') for file in iniFiles: self.cview.generateTreeModel(file) self.loaded_ini = file self.ini_loaded = True except TypeError: print("Could not write and load default '.ini'-file.") try: self.fastafiles = glob.glob('*fasta') if len(self.fastafiles) > 1: QMessageBox.about( self, "Sorry!", "There are multiple '.fasta'-" "files in the specified folder. " "Please choose the one you intent " "to use.") dial = QFileDialog(self) newFilePath = dial.getOpenFileName( self, "Choose .fasta", filePath, "Proteindata (*.fasta)") if newFilePath[0] != '': newFile = newFilePath[0].split("/")[-1] self.fastafiles = [newFile] else: QMessageBox.about( self, "Sorry!", "Nothing was choosen. " "Therefore, no '.fasta'-file " "was loaded. ") self.fastafiles = [] for file in self.fastafiles: self.fview.loadFile(file) self.loaded_fasta = file self.fasta_loaded = True except TypeError: print("Could not load '.fasta'-file.") def setTheme(self): """ Sets theme based on flag state, light or dark modes are possible. Default is light theme. """ p = self.palette if not self.flag: # lightmode p.setColor(QPalette.Window, Qt.white) p.setColor(QPalette.Background, Qt.white) p.setColor(QPalette.WindowText, Qt.black) p.setColor(QPalette.Base, Qt.white) p.setColor(QPalette.AlternateBase, Qt.white) p.setColor(QPalette.ToolTipBase, Qt.black) p.setColor(QPalette.ToolTipText, Qt.black) p.setColor(QPalette.Text, Qt.black) p.setColor(QPalette.Button, Qt.white) p.setColor(QPalette.ButtonText, Qt.black) p.setColor(QPalette.BrightText, Qt.red) p.setColor(QPalette.Link, QColor(213, 125, 37)) p.setColor(QPalette.Highlight, QColor(213, 125, 37)) p.setColor(QPalette.HighlightedText, Qt.white) else: # darkmode p.setColor(QPalette.Window, QColor(53, 53, 53)) p.setColor(QPalette.WindowText, Qt.white) p.setColor(QPalette.Base, QColor(25, 25, 25)) p.setColor(QPalette.AlternateBase, QColor(53, 53, 53)) p.setColor(QPalette.ToolTipBase, Qt.white) p.setColor(QPalette.ToolTipText, Qt.white) p.setColor(QPalette.Text, Qt.white) p.setColor(QPalette.Button, QColor(53, 53, 53)) p.setColor(QPalette.ButtonText, Qt.white) p.setColor(QPalette.BrightText, Qt.red) p.setColor(QPalette.Link, QColor(42, 130, 218)) p.setColor(QPalette.Highlight, QColor(42, 130, 218)) p.setColor(QPalette.HighlightedText, Qt.black) self.setPalette(p) def dragEnterEvent(self, event): e = event data = e.mimeData() urls = data.urls() if urls and urls[0].scheme() == "file": e.acceptProposedAction() else: e.ignore() def dragMoveEvent(self, event): e = event data = e.mimeData() urls = data.urls() if urls and urls[0].scheme() == "file": e.acceptProposedAction() else: e.ignore() def dropEvent(self, event): e = event data = e.mimeData() urls = data.urls() if urls and urls[0].scheme() == "file": #welcome page if self.view.currentIndex() == 0: filetype = "directory" filepath = self.urlHandler(urls[0].path()) if os.path.isdir(filepath): self.loadFunction(filepath) else: self.displayDragNDropError(filetype) #xmlviewer elif self.view.currentIndex() == 1: files = [self.urlHandler(u.path()) for u in urls] self.cview.dragDropEvent(files) #experimental design elif self.view.currentIndex() == 2: filetype = ["mzML", "tsv", "csv"] filepath = self.urlHandler(urls[0].path()) if filepath[-4:] == filetype[0]: self.tview.loadFile(filepath) elif (filepath[-3:] == filetype[1]) or (filepath[-3:] == filetype[2]): self.tview.importBtn(filepath) else: self.displayDragNDropError("", filetype) #fasta viewer elif self.view.currentIndex() == 3: filepath = self.urlHandler(urls[0].path()) filetype = "fasta" if filepath[-5:] == filetype: self.fview.loadFile(filepath) else: self.displayDragNDropError(filetype) #specviewer elif self.view.currentIndex() == 4: filetype = "mzML" filepath = self.urlHandler(urls[0].path()) if filepath[-4:] == filetype: self.sview.sview.openFileDialog(filepath) else: self.displayDragNDropError(filetype) elif self.view.currentIndex() == 5: filetype = "mzTab" filepath = self.urlHandler(urls[0].path()) if filepath[-5:] == filetype: self.xview.readFile(filepath) else: self.displayDragNDropError(filetype) else: e.ignore() def displayDragNDropError(self, filetype: str, mul: list = []): """ displays an error message in a messagebox detailing what went wrong """ message = "" if not mul: if filetype == "directory": message = "a directory to load a project" else: message = "'." + filetype + "'-files" else: message += "'." + mul[0] + "'" for file in mul[1:-1]: message += ", '." + file + "'" message += " or " + mul[-1] + "'-files" dialog = QMessageBox() dialog.setWindowTitle("Error: Invalid File") dialog.setText("Please only use " + message) dialog.setIcon(QMessageBox.Warning) dialog.exec_() def urlHandler(self, url): opsys = platform.system() if (opsys == "Linux"): return str(url) if (opsys == "Windows"): return str(url)[1:] if (opsys == "Darwin"): return str(url) # to be tested def onChange(self): """ this function detects if a tab has been changed. for debugging purposes. """ print(self.view.currentIndex()) def switchTheme(self): """ Toggles between dark and light theme. """ self.flag = not self.flag self.setTheme()
class MainWindow(QMainWindow): OnlineHelpUrl = QUrl("https://GridTools.github.io/serialbox/sdb.html") def __init__(self): super().__init__() Logger.info("Setup main window") self.__input_serializer_data = SerializerData("Input Serializer") self.__input_stencil_data = StencilData(self.__input_serializer_data) self.__reference_serializer_data = SerializerData( "Reference Serializer") self.__reference_stencil_data = StencilData( self.__reference_serializer_data) self.__stencil_field_mapper = StencilFieldMapper( self.__input_stencil_data, self.__reference_stencil_data, GlobalConfig()["async"]) self.__file_system_watcher = QFileSystemWatcher() self.__file_system_watcher.directoryChanged[str].connect( self.popup_reload_box) self.__file_system_watcher_last_modify = time() # Load from session? self.__session_manager = SessionManager() if GlobalConfig()["default_session"]: self.__session_manager.load_from_file() self.__session_manager.set_serializer_data( self.__input_serializer_data) self.__session_manager.set_serializer_data( self.__reference_serializer_data) # Setup GUI self.setWindowTitle('sdb - stencil debugger (%s)' % Version().sdb_version()) self.resize(1200, 600) if GlobalConfig()["center_window"]: self.center() if GlobalConfig()["move_window"]: self.move(GlobalConfig()["move_window"]) self.setWindowIcon(Icon("logo-small.png")) self.init_menu_tool_bar() # Tabs self.__tab_highest_valid_state = TabState.Setup self.__widget_tab = QTabWidget(self) # Setup tab self.__widget_tab.addTab( SetupWindow(self, self.__input_serializer_data, self.__reference_serializer_data), "Setup") # Stencil tab self.__widget_tab.addTab( StencilWindow(self, self.__stencil_field_mapper, self.__input_stencil_data, self.__reference_stencil_data), "Stencil") # Result tab self.__widget_tab.addTab( ResultWindow(self, self.__widget_tab.widget(TabState.Stencil.value), self.__stencil_field_mapper), "Result") # Error tab self.__widget_tab.addTab(ErrorWindow(self), "Error") self.__widget_tab.currentChanged.connect(self.switch_to_tab) self.__widget_tab.setTabEnabled(TabState.Setup.value, True) self.__widget_tab.setTabEnabled(TabState.Stencil.value, False) self.__widget_tab.setTabEnabled(TabState.Result.value, False) self.__widget_tab.setTabEnabled(TabState.Error.value, False) self.__widget_tab.setTabToolTip(TabState.Setup.value, "Setup Input and Refrence Serializer") self.__widget_tab.setTabToolTip( TabState.Stencil.value, "Set the stencil to compare and define the mapping of the fields") self.__widget_tab.setTabToolTip(TabState.Result.value, "View to comparison result") self.__widget_tab.setTabToolTip( TabState.Error.value, "Detailed error desscription of the current field") self.__tab_current_state = TabState.Setup self.set_tab_highest_valid_state(TabState.Setup) self.switch_to_tab(TabState.Setup) self.setCentralWidget(self.__widget_tab) # If the MainWindow is closed, kill all popup windows self.setAttribute(Qt.WA_DeleteOnClose) Logger.info("Starting main loop") self.show() def init_menu_tool_bar(self): Logger.info("Setup menu toolbar") action_exit = QAction("Exit", self) action_exit.setShortcut("Ctrl+Q") action_exit.setStatusTip("Exit the application") action_exit.triggered.connect(self.close) action_about = QAction("&About", self) action_about.setStatusTip("Show the application's About box") action_about.triggered.connect(self.popup_about_box) action_save_session = QAction(Icon("filesave.png"), "&Save", self) action_save_session.setStatusTip("Save current session") action_save_session.setShortcut("Ctrl+S") action_save_session.triggered.connect(self.save_session) action_open_session = QAction(Icon("fileopen.png"), "&Open", self) action_open_session.setShortcut("Ctrl+O") action_open_session.setStatusTip("Open session") action_open_session.triggered.connect(self.open_session) action_help = QAction(Icon("help.png"), "&Online Help", self) action_help.setStatusTip("Online Help") action_help.setToolTip("Online Help") action_help.triggered.connect(self.go_to_online_help) self.__action_continue = QAction(Icon("next_cursor.png"), "Continue", self) self.__action_continue.setStatusTip("Continue to next tab") self.__action_continue.triggered.connect(self.switch_to_next_tab) self.__action_continue.setEnabled(True) self.__action_back = QAction(Icon("prev_cursor.png"), "Back", self) self.__action_back.setStatusTip("Back to previous tab") self.__action_back.triggered.connect(self.switch_to_previous_tab) self.__action_back.setEnabled(False) self.__action_reload = QAction(Icon("step_in.png"), "Reload", self) self.__action_reload.setStatusTip( "Reload Input and Reference Serializer") self.__action_reload.setShortcut("Ctrl+R") self.__action_reload.triggered.connect(self.reload_serializer) self.__action_reload.setEnabled(False) self.__action_try_switch_to_error_tab = QAction( Icon("visualize.png"), "Detailed error description", self) self.__action_try_switch_to_error_tab.setStatusTip( "Detailed error desscription of the current field") self.__action_try_switch_to_error_tab.triggered.connect( self.try_switch_to_error_tab) self.__action_try_switch_to_error_tab.setEnabled(False) menubar = self.menuBar() menubar.setNativeMenuBar(False) self.statusBar() file_menu = menubar.addMenu('&File') file_menu.addAction(action_open_session) file_menu.addAction(action_save_session) file_menu.addAction(action_exit) edit_menu = menubar.addMenu('&Edit') edit_menu.addAction(self.__action_back) edit_menu.addAction(self.__action_continue) edit_menu.addAction(self.__action_reload) help_menu = menubar.addMenu('&Help') help_menu.addAction(action_about) help_menu.addAction(action_help) toolbar = self.addToolBar("Toolbar") toolbar.addAction(action_help) toolbar.addAction(action_open_session) toolbar.addAction(action_save_session) toolbar.addAction(self.__action_back) toolbar.addAction(self.__action_continue) toolbar.addAction(self.__action_reload) toolbar.addAction(self.__action_try_switch_to_error_tab) def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def closeEvent(self, event): self.__session_manager.update_serializer_data( self.__input_serializer_data) self.__session_manager.update_serializer_data( self.__reference_serializer_data) if GlobalConfig()["default_session"]: self.__session_manager.store_to_file() # ===----------------------------------------------------------------------------------------=== # TabWidgets # ==-----------------------------------------------------------------------------------------=== def tab_widget(self, idx): return self.__widget_tab.widget( idx if not isinstance(idx, TabState) else idx.value) def switch_to_tab(self, tab): idx = tab.value if isinstance(tab, TabState) else tab if self.__tab_current_state == TabState(idx): return Logger.info("Switching to %s tab" % TabState(idx).name) self.__tab_current_state = TabState(idx) self.__widget_tab.setCurrentIndex(idx) self.tab_widget(idx).make_update() self.__action_try_switch_to_error_tab.setEnabled( TabState(idx) == TabState.Result) # Error tab is always disabled if not in "Error" self.__widget_tab.setTabEnabled(TabState.Error.value, TabState(idx) == TabState.Error) # First tab if idx == 0: self.__action_continue.setEnabled(True) self.__action_back.setEnabled(False) # Last tab elif idx == self.__widget_tab.count() - 1: self.__action_continue.setEnabled(False) self.__action_back.setEnabled(True) # Middle tab else: self.__action_continue.setEnabled(True) self.__action_back.setEnabled(True) def set_tab_highest_valid_state(self, state): """Set the state at which the data is valid i.e everything <= self.valid_tab_state is valid """ self.__tab_highest_valid_state = state self.enable_tabs_according_to_tab_highest_valid_state() def enable_tabs_according_to_tab_highest_valid_state(self): """Enable/Disable tabs according to self.__tab_highest_valid_state """ if self.__tab_highest_valid_state == TabState.Setup: self.__widget_tab.setTabEnabled(TabState.Setup.value, True) self.__widget_tab.setTabEnabled(TabState.Stencil.value, False) self.__widget_tab.setTabEnabled(TabState.Result.value, False) self.__widget_tab.setTabEnabled(TabState.Error.value, False) self.__action_try_switch_to_error_tab.setEnabled(False) watched_directories = self.__file_system_watcher.directories() if watched_directories: self.__file_system_watcher.removePaths( self.__file_system_watcher.directories()) elif self.__tab_highest_valid_state == TabState.Stencil: self.__file_system_watcher.addPath( self.__input_serializer_data.serializer.directory) self.__file_system_watcher.addPath( self.__reference_stencil_data.serializer.directory) self.__widget_tab.setTabEnabled(TabState.Setup.value, True) self.__widget_tab.setTabEnabled(TabState.Stencil.value, True) self.__widget_tab.setTabEnabled(TabState.Result.value, False) self.__widget_tab.setTabEnabled(TabState.Error.value, False) self.__widget_tab.widget( TabState.Stencil.value).initial_field_match() self.__action_reload.setEnabled(True) self.__action_try_switch_to_error_tab.setEnabled(False) elif self.__tab_highest_valid_state == TabState.Result: self.__widget_tab.setTabEnabled(TabState.Setup.value, True) self.__widget_tab.setTabEnabled(TabState.Stencil.value, True) self.__widget_tab.setTabEnabled(TabState.Result.value, True) self.__widget_tab.setTabEnabled(TabState.Error.value, True) self.__action_try_switch_to_error_tab.setEnabled(True) elif self.__tab_highest_valid_state == TabState.Error: self.__widget_tab.setTabEnabled(TabState.Setup.value, True) self.__widget_tab.setTabEnabled(TabState.Stencil.value, True) self.__widget_tab.setTabEnabled(TabState.Result.value, True) self.__action_try_switch_to_error_tab.setEnabled(False) def switch_to_next_tab(self): self.__widget_tab.currentWidget().make_continue() def switch_to_previous_tab(self): self.__widget_tab.currentWidget().make_back() def try_switch_to_error_tab(self): if self.__widget_tab.widget( TabState.Result.value).try_switch_to_error_tab(): self.__widget_tab.setTabEnabled(TabState.Error.value, True) def error_window_set_result_data(self, result_data): self.__widget_tab.widget( TabState.Error.value).set_result_data(result_data) # ===----------------------------------------------------------------------------------------=== # PopupWidgets # ==-----------------------------------------------------------------------------------------=== def popup_about_box(self): self.__about_widget = PopupAboutWidget(self) def popup_error_box(self, msg): Logger.error( msg.replace("<b>", "").replace("</b>", "").replace("<br />", ":").replace("<br/>", ":")) msg_box = QMessageBox() msg_box.setWindowTitle("Error") msg_box.setIcon(QMessageBox.Critical) msg_box.setText(msg) msg_box.setStandardButtons(QMessageBox.Ok) reply = msg_box.exec_() # Blocking def popup_reload_box(self, path): self.__file_system_watcher.blockSignals(True) reply = QMessageBox.question( self, "Reload serializer?", "The path \"%s\" has changed.\nDo want to reload the serializers?" % path, QMessageBox.Yes | QMessageBox.No, QMessageBox.Yes) if reply == QMessageBox.Yes: self.reload_serializer() self.__file_system_watcher.blockSignals(False) # ===----------------------------------------------------------------------------------------=== # Session manager # ==-----------------------------------------------------------------------------------------=== def save_session(self): Logger.info("Try saving current session") dialog = QFileDialog(self, "Save current session") dialog.setAcceptMode(QFileDialog.AcceptSave) dialog.setDefaultSuffix("json") dialog.setDirectory(getcwd()) if not dialog.exec_(): Logger.info("Abort saving current session") return filename = dialog.selectedFiles() self.__session_manager.update_serializer_data( self.__input_serializer_data) self.__session_manager.update_serializer_data( self.__reference_serializer_data) ret, msglist = self.__session_manager.store_to_file(filename[0]) if not ret: self.popup_error_box("Failed to save configuration file: %s\n%s " % (filename[0], msglist[0])) def open_session(self): Logger.info("Try opening session") filename = QFileDialog.getOpenFileName( self, "Open Session", getcwd(), "JSON configuration (*.json)")[0] if filename is None or filename is "": Logger.info("Abort opening session") return ret, msglist = self.__session_manager.load_from_file(filename) if not ret: self.popup_error_box("Failed to load configuration file: %s\n%s " % (filename, msglist[0])) else: Logger.info("Successfully opened session") self.__session_manager.set_serializer_data( self.__input_serializer_data) self.__session_manager.set_serializer_data( self.__reference_serializer_data) self.switch_to_tab(TabState.Setup) @property def session_manager(self): return self.__session_manager # ===----------------------------------------------------------------------------------------=== # Reload Serializer # ==-----------------------------------------------------------------------------------------=== def reload_serializer(self): Logger.info("Reloading serializers") try: self.__input_serializer_data.reload() self.__reference_serializer_data.reload() if self.__widget_tab.currentIndex() == TabState.Error.value: self.switch_to_tab(TabState.Result) self.__widget_tab.currentWidget().make_update() except RuntimeError as e: self.popup_error_box(str(e)) self.set_tab_highest_valid_state(TabState.Setup) self.switch_to_tab(TabState.Setup) self.__widget_tab.currentWidget().make_update() # ===----------------------------------------------------------------------------------------=== # Online help # ==-----------------------------------------------------------------------------------------=== def go_to_online_help(self): Logger.info("Opening online help") QDesktopServices.openUrl(MainWindow.OnlineHelpUrl)
class Tabs(QWidget, QThread): def __init__(self, callback): super().__init__() self.layout = QHBoxLayout(self) # Change main layout to Vertical # Initialize tab screen self.tabs = QTabWidget() # TODO: This is topright font = QFont(editor['tabFont']) font.setPointSize( editor["tabFontSize"]) # This is the tab font and font size self.tabs.setFont(font) self.IPyconsole = ConsoleWidget( ) # Create IPython widget TODO: This is bottom, this is thread1 self.Console = Console( ) # This is the terminal widget and the SECOND thread self.directory = Directory(callback) # TODO: This is top left self.directory.clearSelection() self.tabCounter = [] # Add tabs self.tab_layout = QHBoxLayout( ) # Create new layout for original tab layout self.tab_layout.addWidget(self.tabs) # Add tab widget to tab layout self.tabs.setTabsClosable(True) self.tabs.setMovable( editor['tabMovable']) # Let's you make the tabs movable if editor[ 'tabShape'] is True: # If tab shape is true then they have this rounded look self.tabs.setTabShape(1) else: self.tabs.setTabShape(0) # If false, it has this boxy look self.tabs.tabCloseRequested.connect(self.closeTab) # Add Console self.console_layout = QHBoxLayout() # Create console layout self.console_layout.addWidget( self.IPyconsole) # Add console to console layout # Build Layout self.layout.addLayout( self.tab_layout) # Adds 'TOP' layout : tab + directory # Creating horizontal splitter self.splitterH = QSplitter(Qt.Horizontal) # Creating vertical splitter self.splitterV = QSplitter(Qt.Vertical) self.splitterV.addWidget(self.splitterH) self.layout.addWidget(self.splitterV) self.splitterV.setSizes([300, 10]) self.setLayout(self.layout) # Sets layout of QWidget self.closeShortcut = QShortcut( QKeySequence(editor["closeTabShortcut"]), self) self.closeShortcut.activated.connect(self.closeTabShortcut) self.hideDirectory() @pyqtSlot() def closeTabShortcut(self): self.index = self.tabs.currentIndex() self.closeTab(self.index) def closeTab(self, index): tab = self.tabs.widget(index) tab.deleteLater() self.tabCounter.pop(index) self.tabs.removeTab(index) def showDirectory(self): self.directory.setVisible(True) self.tab_layout.removeWidget(self.tabs) self.splitterH.addWidget( self.directory ) # Adding that directory widget in the Tab class BEFORE the tabs self.splitterH.addWidget( self.tabs ) # Adding tabs, now the directory tree will be on the left def hideDirectory(self): self.tab_layout.removeWidget(self.directory) self.directory.setVisible(False) """ Because the root layouts are set all you have to do now is just add/remove widgets from the parent layout associated. This keeps the UI order set as intended as built above when initialized. """ def showConsole(self): pass def currentTab(self): return self.tabs.currentWidget()
class Assembler(QMainWindow): def __init__(self, parent=None): super(Assembler, self).__init__(parent) self.resize(800, 600) self.filename = None self.filetuple = None self.dirty = False # Refers to Data Page only. self.nb = None centralwidget = QWidget(self) gridLayout = QGridLayout(centralwidget) self.tabWidget = QTabWidget(centralwidget) # textbox self.tab = QWidget() font = QFont() font.setFamily("Inconsolata") font.setPointSize(14) self.tab.setFont(font) gridLayout_3 = QGridLayout(self.tab) self.plainTextEdit = QPlainTextEdit(self.tab) self.plainTextEdit.installEventFilter(self) self.plainTextEdit.setAcceptDrops(True) gridLayout_3.addWidget(self.plainTextEdit, 0, 0, 1, 1) self.tabWidget.addTab(self.tab, "") self.tab_2 = QWidget() self.tab_2.setFont(font) gridLayout_2 = QGridLayout(self.tab_2) self.plainTextEdit_2 = QPlainTextEdit(self.tab_2) gridLayout_2.addWidget(self.plainTextEdit_2, 0, 0, 1, 1) self.tabWidget.addTab(self.tab_2, "") self.tab_3 = QWidget() self.tab_3.setFont(font) gridLayout_3 = QGridLayout(self.tab_3) self.checkbox = QCheckBox("Cloning genes by tailed primers (no pYPKa_A vectors constructed)") self.checkbox.setChecked(True) gridLayout_3.addWidget(self.checkbox, 0, 0, 0, 0) self.tabWidget.addTab(self.tab_3, "") gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1) self.setCentralWidget(centralwidget) menubar = QMenuBar(self) menubar.setGeometry(QRect(0, 0, 800, 29)) menu_File = QMenu(menubar) self.menu_Solve = QMenu(menubar) self.menu_Help = QMenu(menubar) self.setMenuBar(menubar) self.statusbar = QStatusBar(self) self.setStatusBar(self.statusbar) self.action_New = QAction(self) self.actionSave_As = QAction(self) self.action_Save = QAction(self) self.action_Open = QAction(self) self.action_Quit = QAction(self) self.action_About = QAction(self) self.actionShow_CCPL = QAction(self) self.action_Solve = QAction(self) self.action_OpenNB = QAction(self) self.action_CCPL = QAction(self) self.action_Help = QAction(self) menu_File.addAction(self.action_New) menu_File.addAction(self.action_Open) menu_File.addAction(self.actionSave_As) menu_File.addAction(self.action_Save) menu_File.addSeparator() menu_File.addAction(self.action_Quit) self.menu_Solve.addAction(self.action_Solve) self.menu_Solve.addAction(self.action_OpenNB) self.menu_Help.addAction(self.action_About) #self.menu_Help.addAction(self.action_CCPL) #self.menu_Help.addAction(self.action_Help) menubar.addAction(menu_File.menuAction()) menubar.addAction(self.menu_Solve.menuAction()) menubar.addAction(self.menu_Help.menuAction()) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab),\ "Data Page") self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2),\ "Assembly log") self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_3),\ "Settings") menu_File.setTitle("&File") self.menu_Solve.setTitle("&Assemble") self.menu_Help.setTitle("&About") self.tabWidget.setCurrentIndex(0) self.action_New.setText("&New") self.action_Open.setText("&Open") self.actionSave_As.setText("Save &As") self.action_Save.setText("&Save") self.action_Quit.setText("&Quit") self.action_Solve.setText("&Assemble") self.action_OpenNB.setText("&Open &pathway") self.action_About.setText("&About") #self.action_CCPL.setText("&CCPL") #self.action_Help.setText("&Help") self.action_Quit.triggered.connect(self.close) allToolBar = self.addToolBar("AllToolBar") allToolBar.setObjectName("AllToolBar") self.addActions(allToolBar, (self.action_Open, self.actionSave_As, self.action_Save, self.action_Solve, self.action_OpenNB, self.action_Quit )) self.action_New.triggered.connect(self.fileNew) self.action_Open.triggered.connect(self.fileOpen) self.actionSave_As.triggered.connect(self.fileSaveAs) self.action_Save.triggered.connect(self.fileSave) self.action_Solve.triggered.connect(self.solveAssembly) self.action_OpenNB.triggered.connect(self.openNB) self.action_About.triggered.connect(self.aboutBox) #self.action_CCPL.triggered.connect(self.displayCCPL) #self.action_Help.triggered.connect(self.help) self.plainTextEdit.textChanged.connect(self.setDirty) self.action_New = self.editAction(self.action_New, None,\ 'ctrl+N', 'filenew', 'New File.') self.action_Open = self.editAction(self.action_Open, None, 'ctrl+O', 'fileopen', 'Open File.') self.actionSave_As = self.editAction(self.actionSave_As,\ None, 'ctrl+A', 'filesaveas',\ 'Save and Name File.') self.action_Save = self.editAction(self.action_Save, None, 'ctrl+S', 'filesave', 'Save File.') self.action_Solve = self.editAction(self.action_Solve, None, '', 'solve', 'Assemble.') self.action_OpenNB = self.editAction(self.action_OpenNB, None, '', 'ipynb', 'Open pathway.') self.action_About = self.editAction(self.action_About, None, 'ctrl+B', 'about','Pop About Box.') self.action_CCPL = self.editAction(self.action_CCPL, None, 'ctrl+G', 'licence', 'Show Licence') self.action_Help = self.editAction(self.action_Help, None, 'ctrl+H', 'help', 'Show Help Page.') self.action_Quit = self.editAction(self.action_Quit, None, 'ctrl+Q', 'quit', 'Quit the program.') self.plainTextEdit_2.setReadOnly(True) self.setWindowTitle("ypkpathway") self.setWindowIcon(QIcon( resource_filename("ypkpathway","icons/ypkpathway.png"))) self.plainTextEdit.setFocus() def eventFilter(self, object, event): #print(event.type(), QEvent.DragEnter, object, self.plainTextEdit) if (object is self.plainTextEdit): if (event.type() == QEvent.DragEnter): if event.mimeData().hasUrls(): event.accept() # must accept the dragEnterEvent or else the dropEvent can't occur !!! print("accept") else: event.ignore() print("ignore") if (event.type() == QEvent.Drop): if event.mimeData().hasUrls(): # if file or link is dropped urlcount = len(event.mimeData().urls()) # count number of drops url = event.mimeData().urls()[0] # get first url object.setPlainText('abc') # assign first url to editline event.accept() # doesnt appear to be needed print(456) return True return False # lets the event continue to the edit return False def setDirty(self): '''On change of text in textEdit window, set the flag "dirty" to True''' index = self.tabWidget.currentIndex() if index is not 0: return if self.dirty: return True self.dirty = True self.updateStatus('self.dirty set to True') def clearDirty(self): 'Clear dirty flag' self.dirty = False def fileNew(self): '''Clear both Data Page and Solution Page.''' self.plainTextEdit.setPlainText(' ') self.plainTextEdit_2.setPlainText(' ') self.clearDirty() self.filename = None def okToContinue(self): if self.dirty: reply = QMessageBox.question(self, "Data Loader - Unsaved Changes", "Save unsaved changes?", QMessageBox.Yes|QMessageBox.No|QMessageBox.Cancel) if reply == QMessageBox.Cancel: return False elif reply == QMessageBox.Yes: self.clearDirty() return self.fileSave() return True def okRead(self): 'Pop-up a warning message.' reply = QMessageBox.warning(self, "Warning", '''\nFile Open and Save only in Data Page \n\(Use SaveAs for the Assembly log)''', QMessageBox.Ok) return True def fileOpen(self): '''Open a file in Data Page (with index == 0)''' if self.tabWidget.currentIndex(): self.okRead() return if not self.okToContinue(): return dir_ = (os.path.dirname(str(self.filename)) if self.filename is not None else ".") filetuple = QFileDialog.getOpenFileName(self,"Open File", dir_,) self.filename = filetuple[0] # QFileDialog returns a tuple x with x[0] = file name and # x[1] = type of filter. if self.filename: self.loadFile(self.filename) self.updateStatus('New file opened.') def loadFile(self, fname=None): fl = open(fname, "r") text = fl.read() self.plainTextEdit.setPlainText(text) self.dirty = False def fileSave(self): '''Save file with current file name.''' if self.tabWidget.currentIndex(): self.okRead() return if self.filename is None: return self.fileSaveAs() else: flname = self.filename if flname: tempText = self.plainTextEdit.toPlainText() with open(flname, 'w') as fl: fl.write(tempText) self.dirty = False self.updateStatus('File saved.') return True else: self.updateStatus('Failed to save... ') return False self.filename = None self.dirty = False def fileSaveAs(self): '''Save file with a new name.''' qpr = self.qprintline fname = self.filename or "NoName.txt" self.filename = str(QFileDialog.getSaveFileName(self,"ypkpathway - Save File", fname)) flname = self.filename or "NoName.txt" self.filename = flname fl = open(flname, 'w') tempText = str(self.plainTextEdit.toPlainText()) fl.write(tempText) fl.close() self.dirty = False self.updateStatus('File saved.') def solveAssembly(self): printline = self.qprintline self.plainTextEdit_2.clear() self.tabWidget.setCurrentIndex(1) flbase = os.path.basename(str(self.filename)) title = 'Assembly log for ' + flbase printline('='*len(title)) printline(title) printline('='*len(title)) #print(type(self.plainTextEdit.toPlainText())) #qstringobj = self.plainTextEdit.toPlainText().encode('utf-8') #print(type(qstringobj)) #<class 'PyQt4.QtCore.QString'> #print(qstringobj.toUtf8()[3268:3279]) #print(str(qstringobj.toUtf8()[3268:3279])) #print(type(rawtext), "rawtext") #codec0 = .QTextCodec.codecForName("UTF-16"); #rawtext = unicode(codec0.fromUnicode(tmp), 'UTF-16') #unicode(qstringobj.toUtf8(), encoding="UTF-8").decode() qstringobj = self.plainTextEdit.toPlainText() #import sys;sys.exit(42) pth = parse( qstringobj ) #import sys;sys.exit(42) if len(pth)==0: printline("No of sequences found in Data window") return if self.filename is None: self.fileSaveAs() dir_, ext = os.path.splitext( str(self.filename)) fl, log = ypkpathway.pathway( pth, dir_, pYPKa_A = not self.checkbox.isChecked(), print = printline) if not fl: return with open(os.path.join(dir_, "log.txt"),"w") as f: f.write(log) shutil.copy2( str(self.filename), os.path.join(dir_, "INDATA_"+os.path.basename(str(self.filename)))) printline('') printline('\n\nAssembly finished.') printline('click on the Open pathway button above to open the pathway in the default web browser') self.nb = fl.path def qprintline(self, line): '''Append one line to Solution Page.''' self.plainTextEdit_2.appendPlainText(line.rstrip()) #.decode("utf8")) QApplication.processEvents() def openNB(self): if self.nb: subprocess.Popen(["ipython", "notebook", self.nb]) def aboutBox(self): from PyQt5.QtCore import QT_VERSION_STR from PyQt5.Qt import PYQT_VERSION_STR from sip import SIP_VERSION_STR from ._version import get_versions __version__ = get_versions()["version"][:5] del get_versions from IPython import __version__ as IPython_version QMessageBox.about(self, "About ypkpathway", """<b>Planning of yeast pathway kit constructions.</b> <p>version: {}<br> Copyright 2015-2017 Björn Johansson. This software is released under a BSD style license. This software comes with no warranties expressed or implied.<br><br> Python version: {}<br><br> IPython version: {}<br> Qt version: {}<br> SIP version: {}<br> PyQt version: {}<br> pydna version: {}<br></p> """.format(__version__, sys.version, IPython_version, QT_VERSION_STR, SIP_VERSION_STR, PYQT_VERSION_STR, pydna.__version__[:5])) def displayCCPL(self): '''Read and display CCPL licence.''' self.plainTextEdit.setPlainText(open('CCPL.txt').read()) self.dirty = False self.filename = 'COPYING.txt' self.updateStatus('CCPL displayed.') def help(self): '''Read and display a help file- currently the README.txt.''' self.plainTextEdit.setPlainText(open('README.md').read()) self.dirty = False self.filename = 'README.txt' self.updateStatus('README displayed.') def addActions(self, target, actions): '''Actions are added to Tool Bar.''' for action in actions: if action is None: target.addSeparator() else: target.addAction(action) def editAction(self, action, slot=None, shortcut=None, icon=None, tip=None): '''This method adds to action: icon, shortcut, ToolTip,\ StatusTip and can connect triggered action to slot ''' if icon is not None: action.setIcon(QIcon(":/%s.png" % (icon))) if shortcut is not None: action.setShortcut(shortcut) if tip is not None: action.setToolTip(tip) action.setStatusTip(tip) if slot is not None: action.triggered.connect(slot) return action def qreadline(self, lineNo): '''Read one line from Data Page (lineNo starts with 0)''' return str(self.plainTextEdit.document().\ findBlockByLineNumber(lineNo).text()).rstrip() def updateStatus(self, message): '''Keep status current.''' if self.filename is not None: flbase = os.path.basename(str(self.filename)) self.setWindowTitle(str("ypkpathway - " +\ flbase + "[*]") ) self.statusBar().showMessage(message, 5000) self.setWindowModified(self.dirty)
class RGui(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): # Main window set up self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle("RGui") # File menu self.fileMenu = self.menuBar().addMenu('&File') self.fileMenu.addAction('&Open', self.openFile, Qt.CTRL + Qt.Key_O) self.fileMenu.addAction('&Save figure', self.saveFigure, Qt.SHIFT + Qt.CTRL + Qt.Key_S) # Main widget and its layout # subWidget is embedded in the right half of mainWidget self.mainWidget = QSplitter(Qt.Horizontal, self) self.subWidget = QSplitter(Qt.Vertical, self) # Plot canvas set up, added to layout self.canvas = CanvasWidget(self, width = 10, height = 8, dpi = 100) # Set up the control panel self.ctrPane = QTabWidget(self) self.initCtrPane() # List widget embedded in Tab widget self.selectPane = QTabWidget(self.subWidget) listWidget = QListWidget(self.selectPane) listWidget.setSelectionMode(QAbstractItemView.ExtendedSelection) self.selectPane.addTab(listWidget, 'Untitled') self.firstPlot = True self.spec = [] # Set up the layouts self.mainWidget.addWidget(self.canvas) self.subWidget.addWidget(self.ctrPane) self.subWidget.addWidget(self.selectPane) self.mainWidget.addWidget(self.subWidget) # Set up the MainWindow self.mainWidget.setFocus() self.setCentralWidget(self.mainWidget) self.setGeometry(300, 300, 500, 400) self.statusBar() def initCtrPane(self): # main control set up mainCtr = QFrame(self.ctrPane) mainCtr.setFrameShape(QFrame.StyledPanel) mainCtr.setFrameShadow(QFrame.Sunken) # buttons and controls backSubButton = QPushButton('Background Subtraction', mainCtr) backSubButton.clicked.connect(self.backSub) plotButton = QPushButton('Plot', mainCtr) plotButton.clicked.connect(self.updatePlot1) newTabButton = QPushButton('New tab', mainCtr) newTabButton.clicked.connect(self.addTab) self.plotPeak = QCheckBox('Plot fitted peak', mainCtr) holdPlot = QCheckBox('Hold plot', mainCtr) holdPlot.stateChanged.connect(self.canvas.toggleHold) # layout mainLayout = QGridLayout(mainCtr) mainLayout.addWidget(backSubButton, 0, 0) mainLayout.addWidget(plotButton, 0, 1) mainLayout.addWidget(newTabButton, 1, 0) mainLayout.addWidget(self.plotPeak, 2, 0) mainLayout.addWidget(holdPlot, 2, 1) mainCtr.setLayout(mainLayout) self.ctrPane.addTab(mainCtr, 'Main Control') # NMF control set up NMFCtr = QFrame(self.ctrPane) NMFCtr.setFrameShape(QFrame.StyledPanel) NMFCtr.setFrameShadow(QFrame.Sunken) # input & buttons self.alphaBox = MyDoubleBox(NMFCtr) self.l1RatioBox = MyDoubleBox(NMFCtr) self.loadSettings() NMFButton = QPushButton('NMF', NMFCtr) NMFButton.clicked.connect(self.NMF) # layout NMFLayout = QGridLayout(NMFCtr) NMFLayout.addWidget(QLabel('α'), 0, 0) NMFLayout.addWidget(QLabel('l1 ratio'), 1, 0) NMFLayout.addWidget(self.alphaBox, 0, 1) NMFLayout.addWidget(self.l1RatioBox, 1, 1) NMFLayout.addWidget(NMFButton, 2, 0, 1, 2) NMFCtr.setLayout(NMFLayout) self.ctrPane.addTab(NMFCtr, 'NMF Control') # slots def openFile(self): fileName = QFileDialog.getOpenFileName(self, "Open file") if fileName[0]: self.addSpec(fileName[0], os.path.basename(fileName[0])) def saveFigure(self): fileName = QFileDialog.getSaveFileName(self, "Save current figure") if fileName[0]: self.canvas.saveFigure(fileName) def addSpec(self, fileName, title): # import spectra from file j = self.selectPane.currentIndex() l = self.spec[j].nSpec() listWidget = self.selectPane.currentWidget() self.selectPane.setTabText(j, title) self.spec[j].addSpec(fileName, np.array([[0, 0, 0]])) for i in range(l + 1, self.spec[j]._coord.shape[0]): newItem = QListWidgetItem("[%d %d %d]" % \ tuple(self.spec[j]._coord[i]), listWidget) newItem.setData(Qt.UserRole, QVariant([j, i])) listWidget.addItem(newItem) listWidget.itemDoubleClicked.connect(self.updatePlot2) def addTab(self): listWidget = QListWidget(self.selectPane) listWidget.setSelectionMode(QAbstractItemView.ExtendedSelection) self.selectPane.addTab(listWidget, 'Untitled') self.spec.append(rd.SpecData()) # defines two update plot slots def updatePlot1(self): # slot for multiple items items = self.selectPane.currentWidget().selectedItems() if not items: pass # raise error bArray = items[0].data(Qt.UserRole) n = int(bArray[0]) use = [] for item in items: bArray = item.data(Qt.UserRole) use.append(int(bArray[1])) use = np.array(use).reshape(-1) self.canvas.updatePlot(self.spec[n], use, self.plotPeak.isChecked()) def updatePlot2(self, item): # slot for a single item bArray = item.data(Qt.UserRole) n = int(bArray[0]) i = int(bArray[1]) use = np.array(i).reshape(-1) self.canvas.updatePlot(self.spec[n], use, self.plotPeak.isChecked()) # Here comes the data analysis operations def backSub(self): if self.firstPlot: pass # raise an error fileName = QFileDialog.getOpenFileName(self, "Open background file") if fileName[0]: bg = rd.SpecData(fileName[0]) listWidget = self.selectPane.currentWidget() item = listWidget.item(0) n = item.data(Qt.UserRole)[0] newSpec = self.spec[n].backSub(bg.getSpec(0)) self.addSpec(newSpec, self.currentTabTitle() + '_subtracted') def NMF(self): if self.firstPlot: pass # raise an error item = self.selectPane.currentWidget().item(0) n = item.data(Qt.UserRole)[0] alpha = float(self.alphaBox.text()) l1Ratio = float(self.l1RatioBox.text()) model = self.spec[n].NMF(n_components = 3, init = 'nndsvd', alpha = alpha, l1_ratio = l1Ratio, sparseness = 'data') newSpec = rd.SpecData() newSpec._data = np.append(self.spec[n]._data[[0]], model.components_, axis = 0) newSpec._coord = np.array([[0, 0, 0], [0, 0, 1], [0, 0, 2]]) newSpec._dim = np.array([1, 1, 3]) self.addSpec(newSpec, self.currentTabTitle() + '_NMF') # save settings self.saveSettings() def saveSettings(self): settings = QSettings(QSettings.UserScope, 'Georgia Tech', 'RamanGui', self) settings.setValue('alpha', self.alphaBox.text()) settings.setValue('l1Ratio', self.l1RatioBox.text()) def loadSettings(self): settings = QSettings(QSettings.UserScope, 'Georgia Tech', 'RamanGui', self) if settings.contains('alpha'): self.alphaBox.setText(settings.value('alpha')) if settings.contains('l1Ratio'): self.l1RatioBox.setText(settings.value('l1Ratio')) def currentTabTitle(self): return self.selectPane.tabText(self.selectPane.currentIndex())
class Centro(QWidget): def __init__(self, parent=None): super(Centro, self).__init__() self.widget_abas = None self.menu = None self.indexer = None self.parent = parent self.pacotes = dict() self.temp_build = mkdtemp('build') self.temp_cache = mkdtemp('cache') self.log = None self.init_ui() # noinspection PyUnresolvedReferences def init_ui(self): # Define grid layout = QGridLayout(self) layout.setRowStretch(0, 7.5) layout.setRowStretch(1, 2.5) layout.setColumnMinimumWidth(0, 60) layout.setSpacing(5) layout.setContentsMargins(0, 0, 0, 0) # Cria menu self.menu = Menu.Menu(self) layout.addWidget(self.menu, 0, 0, 2, 2) # Botao para criar nova aba btn = QPushButton(self) btn.clicked.connect(self.nova_aba) btn.setStatusTip("Abrir nova aba") # Cria o widget abas self.widget_abas = QTabWidget(self.parent) self.widget_abas.tabCloseRequested.connect(self.remover_aba) self.widget_abas.setTabsClosable(False) self.widget_abas.setCornerWidget(btn, Qt.TopRightCorner) self.widget_abas.setStyleSheet("background:#252525;") layout.addWidget(self.widget_abas, 0, 1, 1, 2) # Cria log self.log = QPlainTextEdit(self) self.log.setStyleSheet( "border-radius:5px;background:#101010;margin-bottom:5px;margin-right:5px;" ) self.log.setReadOnly(True) self.log.setStatusTip("Log") layout.addWidget(self.log, 1, 1, 1, 2) # Carrega pacotes de hardware self.init_pacotes() # Cria menu de placas self.criar_menu_placas() self.criar_menu_exemplos() # Adiciona a aba de boas vindas self.widget_abas.addTab(BoasVindas(self), "Bem-Vindo") self.show() def init_pacotes(self): """ Carrega os pacotes de hardware do Arduino :return: None """ pasta_hardware = os.path.join('builder', 'hardware') self.indexer = IndexadorContribuicao(os.path.join('builder'), pasta_hardware) self.indexer.parse_index() self.indexer.sincronizar_com_arquivos() self.carregar_hardware(pasta_hardware) self.carregar_hardware_contribuido(self.indexer) self.carregar_hardware( os.path.join(Main.get_caminho_padrao(), 'hardware')) def remover_aba(self, index, fechando=False): """ Remove a aba :param index: Indice da aba :param fechando: Indica se o programa esta fechando default: False :return: None """ if self.widget_abas.count() > 1 or fechando: # Se o index for argumento padrao do sinal (QT) if type(index) is not int: self.remover_aba(self.widget_abas.currentIndex()) else: arquivo = self.widget_abas.widget(index) self.widget_abas.setCurrentIndex(index) if not arquivo.salvo: ret = QMessageBox(self) ret.setText( "Gostaria de salvar este código antes de sair?") ret.setIcon(QMessageBox.Question) ret.addButton("Não Salvar", QMessageBox.NoRole) ret.addButton("Cancelar", QMessageBox.RejectRole) ret.addButton("Salvar", QMessageBox.AcceptRole) ret = ret.exec_() if ret == 1: return False elif ret == 2: self.salvar() if arquivo is not None: arquivo.deleteLater() self.widget_abas.removeTab(index) if self.widget_abas.count() == 1: self.widget_abas.setTabsClosable(False) return True def nova_aba(self, path="", salvar_caminho=True): """ Criar nova aba de editor de texto :param path: Caminho para o arquivo a ser aberto :param salvar_caminho: Se o caminho deve ser definido como local para salvar :return: None """ if self.widget_abas.count() == 0 or path: editor = EditorDeTexto.CodeEditor(self.widget_abas, False, path=path, salvar_caminho=salvar_caminho) else: editor = EditorDeTexto.CodeEditor(self.widget_abas, True, path=path, salvar_caminho=salvar_caminho) if self.widget_abas.count() == 1: self.widget_abas.setTabsClosable(True) identificador_aba = editor.get_nome() if len(identificador_aba) > 10: identificador_aba = identificador_aba[:10] + "..." editor.setStyleSheet("background:#252525") # Adiciona a aba se o arquivo tiver nome if editor.get_nome(): self.widget_abas.addTab(editor, identificador_aba) if editor.get_nome() == "": self.remover_aba(self.widget_abas.count() - 1) else: self.widget_abas.setCurrentIndex(self.widget_abas.count() - 1) # Define que nao eh necessario salvar pois acabou de ser aberto editor.set_salvo(True) def abrir(self, caminho=None, exemplo=True): """ Abrir arquivo .ino ou .brpp em nova aba :param caminho: endereço para abrir :return: None """ if caminho is None or not caminho: salvar_caminho = True dialogo = self.criar_dialogo_arquivo("Abrir arquivo", "Abrir") if dialogo.exec_() == QFileDialog.Accepted: caminho = dialogo.selectedFiles()[0] # Testa se o arquivo existe if os.path.exists(caminho): self.nova_aba(caminho, salvar_caminho) else: QMessageBox(QMessageBox.Warning, "Erro", "O arquivo não existe", QMessageBox.NoButton, self).show() else: self.nova_aba(caminho) widget = self.widget_abas.widget(self.widget_abas.currentIndex()) if exemplo: widget.caminho = "" def salvar(self): """ Salvar arquivo da aba atual :return: None """ editor = self.widget_abas.widget(self.widget_abas.currentIndex()) caminho = editor.get_caminho() # Testa se a aba eh a de boas vindas if caminho == 0: return # Testa se a aba eh a de boas vindas editor.set_salvo(True) if caminho != "": if not os.path.exists(os.path.dirname(caminho)): try: os.makedirs(os.path.dirname(caminho)) except OSError as exc: # Guard against race condition if exc.errno != exc.errno.EEXIST: raise with open(editor.get_caminho(), "w") as arquivo: arquivo.write(editor.get_texto()) else: self.salvar_como() def salvar_como(self): """ Salvar arquivo atual como :return: None """ editor = self.widget_abas.widget(self.widget_abas.currentIndex()) caminho = editor.get_caminho() # Testa se a aba eh a de boas vindas if caminho == 0: return caminho = editor.get_caminho() dialogo = self.criar_dialogo_arquivo('Salvar arquivo', 'Salvar') if dialogo.exec_() == QFileDialog.Accepted: caminho = dialogo.selectedFiles()[0] # Verifica se a pessoa selecionou a pasta ao inves do arquivo em si if not ntpath.basename(caminho).__contains__(".brpp"): caminho = os.path.join(caminho, ntpath.basename(caminho) + ".brpp") # Troca o identificador da aba identificador_aba = ntpath.basename(caminho).replace(".brpp", "") if len(identificador_aba) > 10: identificador_aba = identificador_aba[:10] + "..." self.widget_abas.setTabText(self.widget_abas.currentIndex(), identificador_aba) editor.set_caminho(caminho) self.salvar() def selecionar_texto(self, cursor, texto, indice_inicial, comprimento): """ Seleciona texto :param cursor: Cursor do documento :param texto: Texto a ser selecionado :param indice_inicial: Ponto de onde comecar a busca :param comprimento: Tamanho do texto :return cursor: Cursor com a selecao """ conteudo = self.widget_abas.widget( self.widget_abas.currentIndex()).toPlainText() indice_comeco = conteudo.find(texto, indice_inicial) if indice_comeco == -1: indice_comeco = conteudo.find(texto, 0) if not indice_comeco == -1: cursor.setPosition(indice_comeco, QTextCursor.MoveAnchor) cursor.setPosition(indice_comeco + comprimento, QTextCursor.KeepAnchor) return cursor return -1 def comentar_linha(self): """ comenta a linha :return: None """ editor = self.widget_abas.widget(self.widget_abas.currentIndex()) caminho = editor.get_caminho() # Testa se a aba eh a de boas vindas if caminho == 0: return cursor_atual = editor.textCursor() posicao = cursor_atual.position() bloco_atual = cursor_atual.block() cursor = QTextCursor(bloco_atual) editor.setTextCursor(cursor) texto = bloco_atual.text() if texto.strip().startswith("//"): cursor = self.selecionar_texto(cursor, '/', cursor.position(), 2) cursor.removeSelectedText() cursor.setPosition(posicao - 2) editor.setTextCursor(cursor) else: editor.insertPlainText('//') cursor.setPosition(posicao + 2) editor.setTextCursor(cursor) def achar(self): """ Achar palavra chave no codigo :return: None """ editor = self.widget_abas.widget(self.widget_abas.currentIndex()) caminho = editor.get_caminho() # Testa se a aba eh a de boas vindas if caminho == 0: return texto, ok = QInputDialog.getText(None, "Buscar", "Achar:") if ok and texto != "": cursor = editor.textCursor() cursor = self.selecionar_texto(cursor, texto, cursor.position(), len(texto)) if not cursor == -1: editor.setTextCursor(cursor) def achar_e_substituir(self): """ Achar e substituir palavras chave por outras :return: None """ editor = self.widget_abas.widget(self.widget_abas.currentIndex()) caminho = editor.get_caminho() # Testa se a aba eh a de boas vindas if caminho == 0: return texto, ok = QInputDialog.getText(None, "Achar", "Achar:") subs, ok = QInputDialog.getText(None, "Substituir", "Substituir:") if ok and texto != "": cursor = editor.textCursor() cursor = self.selecionar_texto(cursor, texto, cursor.position(), len(texto)) if not cursor == -1: cursor.removeSelectedText() editor.setTextCursor(cursor) editor.insertPlainText(subs) return @staticmethod def criar_dialogo_arquivo(titulo, acao): """ Cria dialogo personalizado para buscar arquivos :param titulo: Titulo de aba :param acao: Texto do botao de selecionar :return dialogo: dialogo """ dialogo = QFileDialog() dialogo.setWindowTitle(titulo) dialogo.setLabelText(QFileDialog.FileName, "Arquivo:") dialogo.setLabelText(QFileDialog.LookIn, "Buscar em:") dialogo.setLabelText(QFileDialog.FileType, "Tipo de arquivo:") dialogo.setLabelText(QFileDialog.Accept, acao) dialogo.setLabelText(QFileDialog.Reject, "Cancelar") dialogo.setNameFilters( ["Rascunhos Br.ino (*.brpp)", "Rascunhos Arduino (*.ino)"]) dialogo.selectNameFilter("Rascunhos Br.ino (*.brpp)") dialogo.setDirectory(get_caminho_padrao()) return dialogo def abrir_serial(self): """ Abre o monitor serial :return: None """ self.parent.abrir_serial() def carregar_hardware(self, pasta): """ Carrega as opcoes de hardware do Arduino :param pasta: Diretorio do hardware :return: None """ if not os.path.isdir(pasta): return lista = [ os.path.join(pasta, pasta_) for pasta_ in os.listdir(pasta) if os.path.isdir(os.path.join(pasta, pasta_)) ] if len(lista) == 0: return lista = sorted(lista, key=str.lower) lista.remove(os.path.join(pasta, "tools")) for item in lista: nome_item = os.path.basename(item) if nome_item in self.pacotes: pacote_alvo = self.pacotes.get(nome_item) else: pacote_alvo = PacoteAlvo(nome_item) self.pacotes[nome_item] = pacote_alvo self.carregar_pacote_alvo(pacote_alvo, item) def carregar_hardware_contribuido(self, indexer): """ :param indexer: Indexador de contribuicoes :return: None """ for pacote in indexer.criar_pacotes_alvo(): if self.pacotes.get(pacote.get_id(), False): self.pacotes[pacote.get_id()] = pacote @staticmethod def carregar_pacote_alvo(pacote_alvo, pasta): """ Carrega o pacote alvo :param pacote_alvo: Pacote de hardware :param pasta: Diretorio do pacote :return: None """ pastas = os.listdir(pasta) if len(pastas) == 0: return for item in pastas: plataforma_alvo = PlataformaAlvo(item, os.path.join(pasta, item), pacote_alvo) pacote_alvo.get_plataformas()[item] = plataforma_alvo def criar_menu_placas(self): """ Cria o menu das placas :return: None """ self.menus_personalizados = list() titulos_menus_personalizados = list() for pacote_alvo in self.pacotes.values(): for plataforma_alvo in pacote_alvo.get_lista_plataformas(): titulos_menus_personalizados += plataforma_alvo.get_menus( ).values() for titulo_menu_personalizado in titulos_menus_personalizados: menu = QMenu(titulo_menu_personalizado) self.menus_personalizados.append(menu) placas = QActionGroup(self.parent) placas.setExclusive(True) for pacote_alvo in self.pacotes.values(): for plataforma_alvo in pacote_alvo.get_lista_plataformas(): nome = plataforma_alvo.get_preferencias().get("name") self.parent.menu_placas.addAction(QAction(nome, self)) for placa in plataforma_alvo.get_placas().values(): if not placa.get_preferencias().get('hide'): self.parent.menu_placas.addAction( placa.criar_acao(self)) def criar_menu_portas(self): """ Cria o menu das portas :return: None """ for acao in self.parent.menu_portas.actions(): self.parent.menu_portas.removeAction(acao) portas = QActionGroup(self.parent) portas.setExclusive(True) n_portas = len(self.serial_ports()) if n_portas > 0: for porta in self.serial_ports(): porta_acao = Porta.criar_acao(porta, self) self.parent.menu_portas.addAction(porta_acao) if n_portas == 1: Preferencias.set('serial.port', porta) else: self.parent.menu_portas.addAction( QAction("Não há portas disponíveis", self)) def criar_menu_exemplos(self): """ Cria o menu exemplos :return: None """ caminho_exemplos = os.path.join('recursos', 'exemplos') pastas_exemplo = [ x for x in os.listdir(caminho_exemplos) if os.path.isdir(os.path.join(caminho_exemplos, x)) ] pastas_exemplo.sort() for pasta_exemplo in pastas_exemplo: menu = self.parent.menu_exemplos.addMenu(pasta_exemplo) for exemplo in os.listdir( os.path.join(caminho_exemplos, pasta_exemplo)): exemplo_acao = QAction(exemplo, self) caminho_exemplo = os.path.join(caminho_exemplos, pasta_exemplo, exemplo, exemplo + ".brpp") menu.addAction(exemplo_acao) exemplo_acao.triggered.connect( functools.partial(self.abrir, caminho_exemplo, True)) def on_troca_placa_ou_porta(self): """ Troca a placa :return: None """ plataforma = self.get_plataforma_alvo() pastas_bibliotecas = list() # if plataforma: # core = self.get_preferencias_placa() pasta_plataforma = plataforma.get_pasta() pastas_bibliotecas.append(os.path.join(pasta_plataforma, 'libraries')) pastas_bibliotecas.append( os.path.join(get_caminho_padrao(), 'bibliotecas')) def get_preferencias_placa(self): """ Busca as preferencias da palca que esta sendo utilizada :return prefs: Retorna as preferencias """ placa_alvo = self.get_placa_alvo() if placa_alvo is None: return None id_placa = placa_alvo.get_id() prefs = placa_alvo.get_preferencias() nome_extendido = prefs.get("name") for id_menu in placa_alvo.get_ids_menus(): if not placa_alvo.tem_menu(id_menu): continue entrada = Preferencias.get("custom_" + id_menu) if entrada is not None and entrada.startswith(id_placa): id_selecao = entrada[len(id_placa) + 1:] prefs.update( placa_alvo.get_preferencias_menu(id_menu, id_selecao)) nome_extendido += ", " + placa_alvo.get_label_menu( id_menu, id_selecao) prefs['name'] = nome_extendido ferramentas = list() plataforma = self.indexer.get_plataforma_contribuida( self.get_plataforma_alvo()) if plataforma is not None: ferramentas.extend(plataforma.get_ferramentas_resolvidas()) core = prefs.get("build.core") if core is not None and core.__contains__(":"): separado = core.split(":") referenciada = self.get_plataforma_atual_do_pacote(separado[0]) if referenciada is not None: plat_referenciada = self.indexer.get_plataforma_contribuida( referenciada) ferramentas.extend( plat_referenciada.get_ferramentas_resolvidas()) prefix = "runtime.tools." for tool in ferramentas: pasta = tool.get_pasta_instalada() caminho = os.path.abspath(pasta) prefs[(prefix + tool.get_nome() + ".path")] = caminho Preferencias.set(prefix + tool.get_nome() + ".path", caminho) Preferencias.set( prefix + tool.get_nome() + "-" + tool.get_versao() + ".path", caminho) return prefs def get_placa_alvo(self): """ Busca a placa alvo :return placa alvo: """ plataforma_alvo = self.get_plataforma_alvo() if plataforma_alvo: placa = Preferencias.get('board') return plataforma_alvo.get_placa(placa) def get_plataforma_alvo(self, pacote=None, plataforma=None): """ Pega a plataforma alvo :param pacote: Pacote da plataforma :param plataforma: A plataforma :return plataforma_alvo: Plataforma alvo """ if pacote is None: pacote = Preferencias.get('target_package') if plataforma is None: plataforma = Preferencias.get('target_platform') p = self.pacotes.get(pacote) plataforma_alvo = p.get(plataforma) return plataforma_alvo def get_plataforma_atual_do_pacote(self, pacote): """ :param pacote: Pacote da plataforma :return: Retorna a plataforma alvo """ return self.get_plataforma_alvo(pacote, Preferencias.get("target_platform")) def compilar(self): """ Compila o codigo da aba atual :return: None """ self.salvar() self.log.clear() editor = self.widget_abas.widget(self.widget_abas.currentIndex()) caminho = editor.get_caminho() # Testa se a aba eh a de boas vindas if caminho == 0 or caminho == '': return None placa_alvo = self.get_placa_alvo() plataforma_alvo = placa_alvo.get_plataforma() pacote_alvo = plataforma_alvo.get_pacote() # Transforma o codigo brpp em ino traduzir(caminho) resultado = compilar_arduino_builder(caminho, placa_alvo, plataforma_alvo, pacote_alvo, self.temp_build, self.temp_cache) self.log.insertPlainText(str(resultado, sys.stdout.encoding)) def upload(self): """ Compila e carrega o codigo da aba atual :return: None """ self.compilar() editor = self.widget_abas.widget(self.widget_abas.currentIndex()) caminho = editor.get_caminho() # Testa se a aba eh a de boas vindas if caminho == 0 or caminho == '': return None caminho = editor.get_caminho() # Ajustes do Arduino # TODO Terminar ajustes caminho_temp = self.temp_build uploader = None if uploader is None: uploader = Uploader.get_uploader_por_preferencias() uploader = Uploader.UploaderSerial(False) sucesso = False nome = os.path.basename(caminho).replace("brpp", "ino") sucesso = uploader.upload_usando_preferencias(self, caminho_temp, os.path.basename(nome)) @staticmethod def serial_ports(): """ Lista as portas seriais disponiveis :raises EnvironmentError: Plataforma desconhecida ou nao suportada :returns: Lista das portas seriais disponiveis """ if sys.platform.startswith('win'): ports = ['COM%s' % (i + 1) for i in range(256)] elif sys.platform.startswith('linux') or sys.platform.startswith( 'cygwin'): # this excludes your current terminal "/dev/tty" ports = glob.glob('/dev/tty[A-Za-z]*') elif sys.platform.startswith('darwin'): ports = glob.glob('/dev/tty.*') else: raise EnvironmentError('Unsupported platform') result = [] for port in ports: try: s = serial.Serial(port) s.close() result.append(port) except (OSError, serial.SerialException): pass return result def get_menu_personalizado_placa(self, title): for menu in self.menus_personalizados: if menu.title() == title: return menu def instalar_biblioteca(self): caminho_bibliotecas = os.path.join(get_caminho_padrao(), "bibliotecas") dialogo = QFileDialog() dialogo.setWindowTitle("Escolher biblioteca") dialogo.setLabelText(QFileDialog.FileName, "Arquivo:") dialogo.setLabelText(QFileDialog.LookIn, "Buscar em:") dialogo.setLabelText(QFileDialog.FileType, "Tipo de arquivo:") dialogo.setLabelText(QFileDialog.Accept, "Escolher") dialogo.setLabelText(QFileDialog.Reject, "Cancelar") dialogo.setFileMode(QFileDialog.DirectoryOnly) dialogo.setDirectory(get_caminho_padrao()) if dialogo.exec_() == QFileDialog.Accepted: caminho = dialogo.selectedUrls()[0].path() if (caminho.startswith("/") and os.name == 'nt'): caminho = caminho[1:] # Testa se o arquivo existe if os.path.exists(caminho): try: shutil.copytree( caminho, os.path.join(caminho_bibliotecas, os.path.basename(caminho))) # Directories are the same except shutil.Error as e: print('Directory not copied. Error: %s' % e) # Any error saying that the directory doesn't exist except OSError as e: print('Directory not copied. Error: %s' % e) else: QMessageBox(QMessageBox.Warning, "Erro", "O arquivo não existe", QMessageBox.NoButton, self).show() else: return
class Labor_Tabs(QWidget): '''Used to create the labor tabs on the main template ''' previous_row = int labor_total = pyqtSignal(float) def __init__(self, job_number): super().__init__() self.init() self.setWindowTitle('BEI Invoice Number {}'.format(job_number)) def init(self): '''Calls the initialization of the labor tabs ''' self.labor() # self.addTab(self.labor_tab,'Labor Entry') self.previous_row = int self.maximum_row = 0 def labor(self): '''Widget for the labor tabs How it will work: 1.) Open 'Changable_Documents/Labor_Rates.csv' 2.) Read the document in and get the names out of it 3.) Create a dictionary with the names tied to an instantiation of the tabs_technicain 4.) Go through and add set the layouts up ''' #get the current working directory and navigate to the correct folder directory = str( Path( os.path.join( os.path.join(os.environ['USERPROFILE'], 'BEI_Invoices')), 'Basic_Information_Totals')) tech_data = open(str(Path(os.path.join(directory, 'Labor_Rates.csv'))), 'r') information = tech_data.readlines() tech_data.close() self.hourly_wage = {} tech_names = [] for i in range(len(information)): tech_names.append(information[i].split(sep=',')[0]) self.hourly_wage[i] = [ float(information[i].split(sep=',')[1]), float(information[i].split(sep=',')[2]) ] self.technicians = QTabWidget(self) #set up the tab for each technician self.t0 = Labor_Setup().labor_table self.t1 = Labor_Setup().labor_table self.t2 = Labor_Setup().labor_table self.t3 = Labor_Setup().labor_table self.t4 = Labor_Setup().labor_table self.t5 = Labor_Setup().labor_table self.t6 = Labor_Setup().labor_table self.t7 = Labor_Setup().labor_table self.t_tabs = [ self.t0, self.t1, self.t2, self.t3, self.t4, self.t5, self.t6, self.t7 ] for i in range(len(tech_names)): self.technicians.addTab(self.t_tabs[i], tech_names[i]) for j in range(len(tech_names), len(self.t_tabs)): self.t_tabs[j].close() # self.technicians.addTab(self.t1,'David') # self.technicians.addTab(self.t2,'Alan') # self.technicians.addTab(self.t3,'Hanna') self.counts = self.technicians.count() self.total_layout = QVBoxLayout() self.total_layout.addWidget(self.technicians) self.setLayout(self.total_layout) def keyPressEvent(self, event): key = event.key() operator = self.t0 if key == Qt.Key_Return or key == Qt.Key_Enter: try: index = self.technicians.currentIndex() operator = self.determine_tech(index) for cqt in operator.tableWidget.selectedItems(): current_row = cqt.row() # if current_row!=self.previous_row: operator.tableWidget.setCurrentCell(current_row + 1, 0) self.calculate(current_row, operator, index) # self.previous_row=current_row except: pass else: super(Labor_Tabs, self).keyPressEvent(event) def calculate(self, row, current_tech, index): '''Calculate the overtime and regular time for the current row ''' try: regular_hours = float(current_tech.tableWidget.item(row, 3).text()) except: regular_hours = 0 try: ot_hours = float(current_tech.tableWidget.item(row, 4).text()) except: ot_hours = 0 try: hours_wage = float(current_tech.tableWidget.item(row, 5).text()) except: hours_wage = self.hourly_wage[index][0] try: ot_hours_wage = float(current_tech.tableWidget.item(row, 6).text()) except: ot_hours_wage = self.hourly_wage[index][1] labor_t = regular_hours * hours_wage + ot_hours * ot_hours_wage current_tech.tableWidget.setItem(row, 7, QTableWidgetItem(str(labor_t))) total = self.find_tech_total(index) self.labor_total.emit(total) def find_tech_total(self, index): '''Sum up the total amount for the specified tech ''' operator = self.determine_tech(index) total = 0 for i in range(100): try: total += float(operator.tableWidget.item(i, 7).text()) except: break return total def find_tech_individual(self): ''' Find the individual tech labor contribution ''' totals = [] for i in range(self.counts): totals.append(self.find_tech_total(i)) return totals def read_data_out(self, index): operator = self.determine_tech(index) labor_data = [] for i in range(100): try: row = [] for j in range(8): try: float(operator.tableWidget.item(i, 7).text()) if j == 2: row.append( operator.tableWidget.item(i, j).text().replace( ',', '.')) else: row.append(operator.tableWidget.item(i, j).text()) except: row.append('') labor_data.append(row) except: break return labor_data def read_in_data(self, index, data): operator = self.determine_tech(index) for i in range(len(data)): for j in range(len(data[i])): if data[i][j] != '': operator.tableWidget.setItem(i, j, QTableWidgetItem(data[i][j])) def determine_tech(self, index): operator = self.t_tabs[index] # if index==0: # operator=self.t0 # elif index==1: # operator=self.t1 # elif index==2: # operator=self.t2 # elif index==3: # operator=self.t3 return operator
class SetupDialog(QDialog): """ This dialog consists of two parts/tabs: The first one is supposed to help choosing container, device name and mount point to unlock an existing container. The second tab assists in creating a new encrypted LUKS container. """ def __init__(self, parent): """ :param parent: The parent window/dialog used to enable modal behaviour :type parent: :class:`PyQt4.QtGui.QWidget` """ super(SetupDialog, self).__init__(parent, Qt.WindowCloseButtonHint | Qt.WindowTitleHint) self.setWindowTitle(_('luckyLUKS')) self.worker = parent.worker self.is_busy = False # build ui self.layout = QVBoxLayout() self.layout.setSizeConstraint(QLayout.SetFixedSize) style = QApplication.style() # set up stacked layout: initially only the main tab pane (unlock/create) self.main_pane = QStackedWidget() self.tab_pane = QTabWidget() self.main_pane.addWidget(self.tab_pane) self.layout.addWidget(self.main_pane) self.create_pane = None # init in on_create_container() # Unlock Tab unlock_grid = QGridLayout() unlock_grid.setColumnMinimumWidth(1, 220) uheader = QLabel( _('<b>Unlock an encrypted container</b>\n') + _('Please select container file and name')) uheader.setContentsMargins(0, 10, 0, 10) unlock_grid.addWidget(uheader, 0, 0, 1, 3, Qt.AlignCenter) label = QLabel(_('container file')) label.setIndent(5) unlock_grid.addWidget(label, 1, 0) self.unlock_container_file = QLineEdit() unlock_grid.addWidget(self.unlock_container_file, 1, 1) button_choose_file = QPushButton( style.standardIcon(QStyle.SP_DialogOpenButton), '', self) button_choose_file.setToolTip(_('choose file')) unlock_grid.addWidget(button_choose_file, 1, 2) button_choose_file.clicked.connect(self.on_select_container_clicked) label = QLabel(_('device name')) label.setIndent(5) unlock_grid.addWidget(label, 2, 0) self.unlock_device_name = QLineEdit() unlock_grid.addWidget(self.unlock_device_name, 2, 1) # advanced settings a_settings = QExpander(_('Advanced'), self, False) unlock_grid.addWidget(a_settings, 3, 0, 1, 3) label = QLabel(_('key file')) label.setIndent(5) unlock_grid.addWidget(label, 4, 0) self.unlock_keyfile = QLineEdit() unlock_grid.addWidget(self.unlock_keyfile, 4, 1) button_choose_uKeyfile = QPushButton( style.standardIcon(QStyle.SP_DialogOpenButton), '') button_choose_uKeyfile.setToolTip(_('choose keyfile')) unlock_grid.addWidget(button_choose_uKeyfile, 4, 2) button_choose_uKeyfile.clicked.connect( lambda: self.on_select_keyfile_clicked('Unlock')) a_settings.addWidgets([ unlock_grid.itemAtPosition(4, column).widget() for column in range(0, 3) ]) label = QLabel(_('mount point')) label.setIndent(5) unlock_grid.addWidget(label, 5, 0) self.unlock_mountpoint = QLineEdit() unlock_grid.addWidget(self.unlock_mountpoint, 5, 1) button_choose_mountpoint = QPushButton( style.standardIcon(QStyle.SP_DialogOpenButton), '') button_choose_mountpoint.setToolTip(_('choose folder')) unlock_grid.addWidget(button_choose_mountpoint, 5, 2) button_choose_mountpoint.clicked.connect( self.on_select_mountpoint_clicked) a_settings.addWidgets([ unlock_grid.itemAtPosition(5, column).widget() for column in range(0, 3) ]) unlock_grid.setRowStretch(6, 1) unlock_grid.setRowMinimumHeight(6, 10) button_help_unlock = QPushButton( style.standardIcon(QStyle.SP_DialogHelpButton), _('Help')) button_help_unlock.clicked.connect(self.show_help_unlock) unlock_grid.addWidget(button_help_unlock, 7, 2) unlock_tab = QWidget() unlock_tab.setLayout(unlock_grid) self.tab_pane.addTab(unlock_tab, _('Unlock Container')) # Create Tab create_grid = QGridLayout() cheader = QLabel( _('<b>Create a new encrypted container</b>\n') + _('Please choose container file, name and size')) cheader.setContentsMargins(0, 10, 0, 10) create_grid.addWidget(cheader, 0, 0, 1, 3, Qt.AlignCenter) label = QLabel(_('container file')) label.setIndent(5) create_grid.addWidget(label, 1, 0) self.create_container_file = QLineEdit() create_grid.addWidget(self.create_container_file, 1, 1) button_choose_file = QPushButton( style.standardIcon(QStyle.SP_DialogOpenButton), '') button_choose_file.setToolTip(_('set file')) create_grid.addWidget(button_choose_file, 1, 2) button_choose_file.clicked.connect(self.on_save_container_clicked) label = QLabel(_('device name')) label.setIndent(5) create_grid.addWidget(label, 2, 0) self.create_device_name = QLineEdit() create_grid.addWidget(self.create_device_name, 2, 1) label = QLabel(_('container size')) label.setIndent(5) create_grid.addWidget(label, 3, 0) self.create_container_size = QSpinBox() self.create_container_size.setRange(1, 1000000000) self.create_container_size.setValue(1) create_grid.addWidget(self.create_container_size, 3, 1) self.create_size_unit = QComboBox() self.create_size_unit.addItems(['MB', 'GB']) self.create_size_unit.setCurrentIndex(1) create_grid.addWidget(self.create_size_unit, 3, 2) # advanced settings a_settings = QExpander(_('Advanced'), self, False) create_grid.addWidget(a_settings, 4, 0, 1, 3) label = QLabel(_('key file')) label.setIndent(5) create_grid.addWidget(label, 5, 0) self.create_keyfile = QLineEdit() create_grid.addWidget(self.create_keyfile, 5, 1) button_choose_cKeyfile = QPushButton( style.standardIcon(QStyle.SP_DialogOpenButton), '') button_choose_cKeyfile.setToolTip(_('choose keyfile')) create_grid.addWidget(button_choose_cKeyfile, 5, 2) button_choose_cKeyfile.clicked.connect( lambda: self.on_select_keyfile_clicked('Create')) a_settings.addWidgets([ create_grid.itemAtPosition(5, column).widget() for column in range(0, 3) ]) button_create_keyfile = QPushButton(_('Create key file')) button_create_keyfile.clicked.connect(self.on_create_keyfile) create_grid.addWidget(button_create_keyfile, 6, 1) a_settings.addWidgets([button_create_keyfile]) label = QLabel(_('format')) label.setIndent(5) create_grid.addWidget(label, 7, 0) self.create_encryption_format = QComboBox() self.create_encryption_format.addItem('LUKS') self.create_encryption_format.addItem('TrueCrypt') if not is_installed('tcplay'): self.create_encryption_format.setEnabled(False) self.create_encryption_format.setCurrentIndex(0) create_grid.addWidget(self.create_encryption_format, 7, 1) a_settings.addWidgets([ create_grid.itemAtPosition(7, column).widget() for column in range(0, 2) ]) label = QLabel(_('filesystem')) label.setIndent(5) create_grid.addWidget(label, 8, 0) filesystems = ['ext4', 'ext2', 'ntfs'] self.create_filesystem_type = QComboBox() for filesystem in filesystems: if is_installed('mkfs.' + filesystem): self.create_filesystem_type.addItem(filesystem) self.create_filesystem_type.setCurrentIndex(0) create_grid.addWidget(self.create_filesystem_type, 8, 1) a_settings.addWidgets([ create_grid.itemAtPosition(8, column).widget() for column in range(0, 2) ]) create_grid.setRowStretch(9, 1) create_grid.setRowMinimumHeight(9, 10) button_help_create = QPushButton( style.standardIcon(QStyle.SP_DialogHelpButton), _('Help')) button_help_create.clicked.connect(self.show_help_create) create_grid.addWidget(button_help_create, 10, 2) create_tab = QWidget() create_tab.setLayout(create_grid) self.tab_pane.addTab(create_tab, _('Create New Container')) self.tab_pane.currentChanged.connect(self.on_switchpage_event) # OK and Cancel buttons self.buttons = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel, parent=self) self.buttons.button(QDialogButtonBox.Ok).setText(_('Unlock')) self.buttons.accepted.connect(self.on_accepted) self.buttons.rejected.connect(self.reject) self.layout.addWidget(self.buttons) # ui built, add to widget self.setLayout(self.layout) def on_create_container(self): """ Triggered by clicking create. Hides the unlock/create pane and switches to a status pane where the progress in creating the new container can be followed step by step. This shows the header and the first step: Initializing the container file with random data """ self.init_create_pane() header = QLabel( _('<b>Creating new container</b>\n') + _('patience .. this might take a while')) header.setContentsMargins(0, 10, 0, 10) self.create_status_grid.addWidget(header, 0, 0, 1, 3, Qt.AlignCenter) self.create_status_grid.addWidget( QLabel('<b>' + _('Step') + ' 1/3</b>'), 1, 0) self.create_status_grid.addWidget( QLabel(_('Initializing Container File')), 1, 1) self.create_progressbars.append(QProgressBar()) self.create_progressbars[0].setRange(0, 100) self.create_status_grid.addWidget(self.create_progressbars[0], 2, 0, 1, 3) self.create_status_grid.setRowStretch(7, 1) # top align # add to stack widget and switch display self.main_pane.addWidget(self.create_pane) self.main_pane.setCurrentIndex(1) # calculate designated container size for worker and progress indicator size = self.create_container_size.value() size = size * ( 1024 * 1024 * 1024 if self.create_size_unit.currentIndex() == 1 else 1024 * 1024) # GB vs MB location = self.encode_qt_output(self.create_container_file.text()) if not os.path.dirname(location): location = os.path.join(os.path.expanduser('~'), location) self.create_container_file.setText(location) # start timer for progressbar updates during container creation self.create_timer.timeout.connect( lambda: self.display_progress_percent(location, size)) self.create_timer.start(500) self.worker.execute(command={ 'type': 'request', 'msg': 'create', 'device_name': self.encode_qt_output(self.create_device_name.text()), 'container_path': location, 'container_size': size, 'key_file': self.encode_qt_output(self.create_keyfile.text()) if self.create_keyfile.text() != '' else None, 'filesystem_type': str(self.create_filesystem_type.currentText()), 'encryption_format': str(self.create_encryption_format.currentText()), }, success_callback=self.on_luksFormat_prompt, error_callback=lambda msg: self. display_create_failed(msg, stop_timer=True)) def on_luksFormat_prompt(self, msg): """ Triggered after the container file is created on disk Shows information about the next step and asks the user for the passphrase to be used with the new container """ self.set_progress_done(self.create_timer, self.create_progressbars[0]) self.create_status_grid.addWidget( QLabel('<b>' + _('Step') + ' 2/3</b>'), 3, 0) self.create_status_grid.addWidget(QLabel(_('Initializing Encryption')), 3, 1) self.create_progressbars.append(QProgressBar()) self.create_progressbars[1].setRange(0, 0) self.create_status_grid.addWidget(self.create_progressbars[1], 4, 0, 1, 3) if msg == 'getPassword': try: self.worker.execute( command={ 'type': 'response', 'msg': FormatContainerDialog(self).get_password() }, success_callback=self.on_creating_filesystem, error_callback=self.display_create_failed) except UserInputError: # user cancelled dlg self.worker.execute({ 'type': 'abort', 'msg': '' }, None, None) # notify worker process self.display_create_failed(_('Initialize container aborted')) else: # using keyfile self.worker.execute(command={ 'type': 'response', 'msg': '' }, success_callback=self.on_creating_filesystem, error_callback=self.display_create_failed) def on_creating_filesystem(self, msg): """ Triggered after LUKS encryption got initialized. Shows information about the last step """ self.set_progress_done(progressbar=self.create_progressbars[1]) self.create_status_grid.addWidget( QLabel('<b>' + _('Step') + ' 3/3</b>'), 5, 0) self.create_status_grid.addWidget(QLabel(_('Initializing Filesystem')), 5, 1) self.create_progressbars.append(QProgressBar()) self.create_progressbars[2].setRange(0, 0) self.create_status_grid.addWidget(self.create_progressbars[2], 6, 0, 1, 3) self.worker.execute(command={ 'type': 'response', 'msg': '' }, success_callback=self.display_create_success, error_callback=self.display_create_failed) def display_create_success(self, msg): """ Triggered after successful creation of a new container """ self.set_progress_done(progressbar=self.create_progressbars[2]) # copy values of newly created container to unlock dlg und reset create values self.unlock_container_file.setText(self.create_container_file.text()) self.unlock_device_name.setText(self.create_device_name.text()) self.unlock_keyfile.setText(self.create_keyfile.text()) show_info( self, _('<b>{device_name}\nsuccessfully created!</b>\nClick on unlock to use the new container' ).format(device_name=self.encode_qt_output( self.create_device_name.text())), _('Success')) # reset create ui and switch to unlock tab self.create_container_file.setText('') self.create_device_name.setText('') self.create_container_size.setValue(1) self.create_size_unit.setCurrentIndex(1) self.create_keyfile.setText('') self.create_encryption_format.setCurrentIndex(0) self.create_filesystem_type.setCurrentIndex(0) self.display_create_done() self.tab_pane.setCurrentIndex(0) def display_create_failed(self, errormessage, stop_timer=False): """ Triggered when an error happend during the create process :param errormessage: errormessage to be shown :type errormessage: str :param stop_timer: stop a progress indicator? :type stop_timer: bool """ if stop_timer: self.set_progress_done(self.create_timer) show_alert(self, errormessage) self.display_create_done() def display_create_done(self): """ Helper to hide the create process informations and show the unlock/create pane """ self.is_busy = False self.main_pane.setCurrentIndex(0) self.buttons.setEnabled(True) def set_progress_done(self, timeout=None, progressbar=None): """ Helper to end stop the progress indicator :param timeout: Timer to stop :type timeout: QTimer or None :param progressbar: progressbar widget to set to 'Done' :type progressbar: :class:`QProgressBar` or None """ if timeout is not None: timeout.stop() if progressbar is not None: if not progressbar.maximum(): progressbar.setRange(0, 100) progressbar.setValue(100) progressbar.setFormat(_('Done')) def on_accepted(self): """ Event handler for response: Start unlock or create action """ try: if self.tab_pane.currentIndex() == 1: self.on_create_container() else: UnlockContainerDialog( self, self.worker, self.get_luks_device_name(), self.get_encrypted_container(), self.get_keyfile(), self.get_mount_point()).communicate() # blocks # optionally create startmenu entry self.show_create_startmenu_entry() # all good, now switch to main window self.accept() except UserInputError as error: show_alert(self, format_exception(error)) def init_create_pane(self): """ Helper that initializes the ui for the progress indicators shown while creating containers or keyfiles """ self.is_busy = True self.create_progressbars = [] self.create_timer = QTimer(self) self.buttons.setEnabled(False) if self.main_pane.count() > 1: # remove previous create display if any self.main_pane.removeWidget(self.create_pane) # built ui self.create_pane = QWidget() self.create_status_grid = QGridLayout() self.create_pane.setLayout(self.create_status_grid) self.create_status_grid.setVerticalSpacing(5) def on_create_keyfile(self): """ Triggered by clicking the `create key file` button below the key file text field (create) Asks for key file location if not already provided, creates the progress ui and starts a create-thread """ if self.create_keyfile.text() == '': key_file = self.encode_qt_output( self.on_save_file(_('new_keyfile.bin'))) else: key_file = self.encode_qt_output(self.create_keyfile.text()) if not os.path.dirname(key_file): key_file = os.path.join(os.path.expanduser('~'), key_file) self.init_create_pane() header = QLabel(_('<b>Creating key file</b>')) self.create_status_grid.addWidget(header, 1, 0, 1, 3, Qt.AlignCenter) header.setContentsMargins(0, 30, 0, 10) self.create_progressbars.append(QProgressBar()) self.create_progressbars[0].setRange(0, 100) self.create_status_grid.addWidget(self.create_progressbars[0], 2, 0, 1, 3) info = QLabel( _('This might take a while. Since computers are deterministic machines\n' 'it is quite a challenge to generate real random data for the key.\n' '\n' 'You can speed up the process by typing, moving the mouse\n' 'and generally use the computer while the key gets generated.')) info.setContentsMargins(0, 10, 0, 10) self.create_status_grid.addWidget(info, 3, 0, 1, 3, Qt.AlignCenter) self.create_status_grid.setRowStretch(4, 2) # vertical align # add to stack widget and switch display self.main_pane.addWidget(self.create_pane) self.main_pane.setCurrentIndex(1) # start timer for progressbar updates during keyfile creation self.create_timer.timeout.connect( lambda: self.display_progress_percent(key_file, 1024)) self.create_timer.start(500) # run QThread with keyfile creation from luckyLUKS.utils import KeyfileCreator self.create_thread = KeyfileCreator(self, key_file) self.create_thread.start() def on_keyfile_created(self, key_file_path): """ Triggered when key file creation was successful. Restores the normal setup ui """ self.set_progress_done(self.create_timer, progressbar=self.create_progressbars[0]) show_info( self, _('<b>{key_file}\nsuccessfully created!</b>\n' 'You can use this key file now,\n' 'to create a new container.').format(key_file=key_file_path), _('Success')) self.display_create_done() self.create_keyfile.setText(key_file_path) def show_create_startmenu_entry(self): """ Shown after successfull unlock with setup dialog -> ask for shortcut creation """ message = (_('<b>Successfully unlocked!</b>\n\n' 'Do you want to create\n' 'a startup menu entry for <b>{device_name}</b>?\n\n' '-> Your password will NOT be saved!\n' ' This just creates a shortcut,\n' ' to the unlock container dialog.\n').format( device_name=self.get_luks_device_name())) mb = QMessageBox(QMessageBox.Question, '', message, QMessageBox.Ok | QMessageBox.Cancel, self) mb.button(QMessageBox.Ok).setText(_('Create shortcut')) mb.button(QMessageBox.Cancel).setText(_('No, thanks')) if mb.exec_() == QMessageBox.Ok: self.create_startmenu_entry() def create_startmenu_entry(self): """ Creates a startmenu entry that lets the user skip the setup dialog and go directly to the main UI Includes a workaround for the safety net some desktop environments create around the startupmenu """ import random import string # command to be saved in shortcut: calling the script with the arguments entered in the dialog # put all arguments in single quotes and escape those in the strings (shell escape ' -> '\'') cmd = os.path.abspath(sys.argv[0]) cmd += " -c '" + self.get_encrypted_container().replace("'", "'\\'''") + "'" cmd += " -n '" + self.get_luks_device_name().replace("'", "'\\'''") + "'" if self.get_mount_point() is not None: cmd += " -m '" + self.get_mount_point().replace("'", "'\\'''") + "'" if self.get_keyfile() is not None: cmd += " -k '" + self.get_keyfile().replace("'", "'\\'''") + "'" # create .desktop-file filename = _('luckyLUKS') + '-' + ''.join( i for i in self.get_luks_device_name() if i not in ' \/:*?<>|' ) # xdg-desktop-menu has problems with some special chars if is_installed('xdg-desktop-menu' ): # create in tmp and add freedesktop menu entry # some desktop menus dont delete the .desktop files if a user removes items from the menu but keep track of those files instead # those items wont be readded later, the random part of the filename works around this behaviour desktop_file_path = os.path.join( '/tmp', filename + '-' + ''.join(random.choice(string.ascii_letters) for i in range(4)) + '.desktop') else: # or create in users home dir desktop_file_path = os.path.join(os.path.expanduser('~'), filename + '.desktop') desktop_file = codecs.open(desktop_file_path, 'w', 'utf-8') entry_name = _('Unlock {device_name}').format( device_name=self.get_luks_device_name()) desktop_file.write("[Desktop Entry]\n") desktop_file.write("Name=" + entry_name + "\n") desktop_file.write("Comment=" + self.get_luks_device_name() + " " + _('Encrypted Container Tool') + "\n") desktop_file.write("GenericName=" + _('Encrypted Container') + "\n") desktop_file.write("Categories=Utility;\n") desktop_file.write("Exec=" + cmd + "\n") desktop_file.write("Icon=dialog-password\n") desktop_file.write("NoDisplay=false\n") desktop_file.write("StartupNotify=false\n") desktop_file.write("Terminal=0\n") desktop_file.write("TerminalOptions=\n") desktop_file.write("Type=Application\n\n") desktop_file.close() os.chmod(desktop_file_path, 0o700) # some distros need the xbit to trust the desktop file if is_installed('xdg-desktop-menu'): # safest way to ensure updates: explicit uninstall followed by installing a new desktop file with different random part import glob for desktopfile in glob.glob( os.path.expanduser('~') + '/.local/share/applications/' + filename + '-*.desktop'): with open(os.devnull) as DEVNULL: subprocess.call( ['xdg-desktop-menu', 'uninstall', desktopfile], stdout=DEVNULL, stderr=subprocess.STDOUT) try: subprocess.check_output([ 'xdg-desktop-menu', 'install', '--novendor', desktop_file_path ], stderr=subprocess.STDOUT, universal_newlines=True) os.remove(desktop_file_path) # remove from tmp show_info( self, _('<b>` {name} `</b>\nadded to start menu').format( name=entry_name), _('Success')) except subprocess.CalledProcessError as cpe: home_dir_path = os.path.join( os.path.expanduser('~'), os.path.basename(desktop_file_path)) # move to homedir instead from shutil import move move(desktop_file_path, home_dir_path) show_alert(self, cpe.output) show_info( self, _('Adding to start menu not possible,\nplease place your shortcut manually.\n\nDesktop file saved to\n{location}' ).format(location=home_dir_path)) else: show_info( self, _('Adding to start menu not possible,\nplease place your shortcut manually.\n\nDesktop file saved to\n{location}' ).format(location=desktop_file_path)) def reject(self): """ Event handler cancel: Ask for confirmation while creating container """ if self.confirm_close(): super(SetupDialog, self).reject() def closeEvent(self, event): """ Event handler close: ask for confirmation while creating container """ if not self.confirm_close(): event.ignore() def confirm_close(self): """ Displays a confirmation dialog if currently busy creating container or keyfile :returns: The users decision or True if no create process running :rtype: bool """ if self.is_busy: message = _( 'Currently processing your request!\nDo you really want to quit?' ) mb = QMessageBox(QMessageBox.Question, '', message, QMessageBox.Ok | QMessageBox.Cancel, self) mb.button(QMessageBox.Ok).setText(_('Quit')) return mb.exec_() == QMessageBox.Ok else: return True def on_switchpage_event(self, index): """ Event handler for tab switch: change text on OK button (Unlock/Create) """ new_ok_label = _('Unlock') if index == 1: # create if self.create_filesystem_type.currentText() == '': show_alert( self, _('No tools to format the filesystem found\n' 'Please install, eg for Debian/Ubuntu\n' '`apt-get install e2fslibs ntfs-3g`')) new_ok_label = _('Create') self.buttons.button(QDialogButtonBox.Ok).setText(new_ok_label) def on_select_container_clicked(self): """ Triggered by clicking the select button next to container file (unlock) """ file_path = QFileDialog.getOpenFileName( self, _('Please choose a container file'), os.getenv("HOME")) if isinstance(file_path, tuple): # qt5 returns tuple path/selected filter file_path = file_path[0] self.unlock_container_file.setText(file_path) self.buttons.button(QDialogButtonBox.Ok).setText(_('Unlock')) def on_select_mountpoint_clicked(self): """ Triggered by clicking the select button next to mount point """ self.unlock_mountpoint.setText( QFileDialog.getExistingDirectory( self, _('Please choose a folder as mountpoint'), os.getenv("HOME"))) self.buttons.button(QDialogButtonBox.Ok).setText(_('Unlock')) def on_select_keyfile_clicked(self, tab): """ Triggered by clicking the select button next to key file (both unlock and create tab) """ file_path = QFileDialog.getOpenFileName(self, _('Please choose a key file'), os.getenv("HOME")) if isinstance(file_path, tuple): # qt5 returns tuple path/selected filter file_path = file_path[0] if tab == 'Unlock': self.unlock_keyfile.setText(file_path) elif tab == 'Create': self.create_keyfile.setText(file_path) self.buttons.button(QDialogButtonBox.Ok).setText(_(tab)) def on_save_container_clicked(self): """ Triggered by clicking the select button next to container file (create) Uses a file dialog to set the path of the container file to be created """ self.create_container_file.setText( self.on_save_file(_('new_container.bin'))) def on_save_file(self, default_filename): """ Opens a native file dialog and returns the chosen path of the file to be saved The dialog does not allow overwriting existing files - to get this behaviour while using native dialogs the QFileDialog has to be reopened on overwrite. A bit weird but enables visual consistency with the other file choose dialogs :param default_filename: The default filename to be used in the Qt file dialog :type default_filename: str/unicode :returns: The designated key file path :rtype: str/unicode """ def_path = os.path.join(os.getenv("HOME"), default_filename) while True: save_path = QFileDialog.getSaveFileName( self, _('Please create a new file'), def_path, options=QFileDialog.DontConfirmOverwrite) save_path = self.encode_qt_output(save_path[0]) if isinstance( save_path, tuple) else self.encode_qt_output(save_path) self.buttons.button(QDialogButtonBox.Ok).setText( _('Create')) # qt keeps changing this.. if os.path.exists(save_path): show_alert( self, _('File already exists:\n{filename}\n\n' '<b>Please create a new file!</b>').format( filename=save_path)) def_path = os.path.join(os.path.basename(save_path), default_filename) else: return save_path def display_progress_percent(self, location, size): """ Update value on the container creation progress bar :param location: The path of the container file currently being created :type location: str :param size: The final size the new container in bytes :type size: int """ try: new_value = int(os.path.getsize(location) / size * 100) except Exception: new_value = 0 self.create_progressbars[0].setValue(new_value) def get_encrypted_container(self): """ Getter for QLineEdit text returns python unicode (instead of QString in py2) :returns: The container file path :rtype: str/unicode """ return self.encode_qt_output(self.unlock_container_file.text()) def get_luks_device_name(self): """ Getter for QLineEdit text returns python unicode (instead of QString in py2) :returns: The device name :rtype: str/unicode """ return self.encode_qt_output(self.unlock_device_name.text()) def get_keyfile(self): """ Getter for QLineEdit text returns python unicode (instead of QString in py2) :returns: The mount point path :rtype: str/unicode or None """ kf = self.encode_qt_output(self.unlock_keyfile.text()) return kf if kf != '' else None def get_mount_point(self): """ Getter for QLineEdit text returns python unicode (instead of QString in py2) :returns: The mount point path :rtype: str/unicode or None """ mp = self.encode_qt_output(self.unlock_mountpoint.text()) return mp if mp != '' else None def encode_qt_output(self, qstring_or_str): """ Normalize output from QLineEdit :param qstring_or_str: Output from QLineEdit.text() :type qstring_or_str: str/QString :returns: python unicode (instead of QString in py2) :rtype: str/unicode """ try: return qstring_or_str.strip() except AttributeError: # py2: 'QString' object has no attribute strip return unicode(qstring_or_str.trimmed().toUtf8(), encoding="UTF-8") def show_help_create(self): """ Triggered by clicking the help button (create tab) """ header_text = _('<b>Create a new encrypted container</b>\n') basic_help = _( 'Enter the path of the <b>new container file</b> in the textbox ' 'or click the button next to the box for a graphical create file dialog.' '\n\n' 'The <b>device name</b> will be used to identify the unlocked container. ' 'It can be any name up to 16 unicode characters, as long as it is unique.' '\n\n' 'The <b>size</b> of the container can be provided in GB or MB. The container ' 'will get initialized with random data, this can take quite a while - ' '1 hour for a 10GB container on an external drive is nothing unusual.' ) advanced_topics = [{ 'head': _('key file'), 'text': _('A key file can be used to allow access to an encrypted container instead of a password. ' 'Using a key file resembles unlocking a door with a key in the real world - anyone with ' 'access to the key file can open your encrypted container. Make sure to store it at a ' 'protected location. Its okay to store it on your computer if you are using an already ' 'encrypted harddrive or a digital keystore. Having the key file on a ' '<a href="https://www.google.com/search?q=keychain+usb+drive">small USB drive</a> ' 'attached to your real chain of keys would be an option as well.\n' 'Since you dont have to enter a password, using a key file can be a convenient way to ' 'access your encrypted container. Just make sure you dont lose the key (file) ;)' ) + _('\n\n' 'Although basically any file could be used as a key file, a file with predictable content ' 'leads to similar problems as using weak passwords. Audio files or pictures are a good choice. ' 'If unsure use the `create key file` button to generate a small key file filled with random data.' ) }, { 'head': _('encryption format'), 'text': _('The standard disk encryption format on Linux is called LUKS. ' 'With <a href="https://github.com/t-d-k/doxbox">doxbox</a> you can use LUKS containers on Windows as well. ' 'The TrueCrypt format is quite popular on Windows/Mac, and can be created ' 'on Linux if `tcplay` is installed. Please note, that "hidden" TrueCrypt ' 'partitions are not supported by luckyLUKS!') }, { 'head': _('filesystem'), 'text': _('Choose the ntfs filesystem to be able to access your data from Linux, ' 'Windows and Mac OSX. Since access permissions cannot be mapped from ' 'ntfs to Linux, access to ntfs devices is usually not restricted ' '-> take care when using unlocked ntfs devices in a multiuser environment!' ) }] hd = HelpDialog(self, header_text, basic_help, advanced_topics) hd.exec_() def show_help_unlock(self): """ Triggered by clicking the help button (unlock tab) """ header_text = _('<b>Unlock an encrypted container</b>\n') basic_help = _( 'Select the encrypted <b>container file</b> by clicking the button next to ' 'the textbox. Both LUKS and Truecrypt containers are supported!' '\n\n' 'The <b>device name</b> will be used to identify the unlocked container. ' 'It can be any name up to 16 unicode characters, as long as it is unique ' '-> you cannot give two unlocked containers the same name') advanced_topics = [{ 'head': _('key file'), 'text': _('A key file can be used to allow access to an encrypted container instead of a password. ' 'Using a key file resembles unlocking a door with a key in the real world - anyone with ' 'access to the key file can open your encrypted container. Make sure to store it at a ' 'protected location. Its okay to store it on your computer if you are using an already ' 'encrypted harddrive or a digital keystore. Having the key file on a ' '<a href="https://www.google.com/search?q=keychain+usb+drive">small USB drive</a> ' 'attached to your real chain of keys would be an option as well.\n' 'Since you dont have to enter a password, using a key file can be a convenient way to ' 'access your encrypted container. Just make sure you dont lose the key (file) ;)' ) }, { 'head': _('mount point'), 'text': _('The mount point is the folder on your computer, where you can ' 'access the files inside the container after unlocking. ' 'If automatic mounting is configured on your system (eg with udisks), ' 'explicitly setting a mountpoint is not neccessary (but still possible).' ) }] hd = HelpDialog(self, header_text, basic_help, advanced_topics) hd.exec_()
class materials_main(QWidgetSavePos): def changed_click(self): if self.notebook.tabText(self.notebook.currentIndex()).strip() == _( "Electrical parameters"): help_window().help_set_help([ "tab.png", _("<big><b>Electrical parameters</b></big><br>Use this tab to configure the electrical parameters for the material." ) ]) self.ribbon.tb_save.setEnabled(False) self.ribbon.import_data.setEnabled(False) if self.notebook.tabText( self.notebook.currentIndex()).strip() == _("Luminescence"): help_window().help_set_help([ "tab.png", _("<big><b>Luminescence</b></big><br>Use this tab to edit the materials Luminescence." ) ]) self.ribbon.tb_save.setEnabled(False) self.ribbon.import_data.setEnabled(False) if self.notebook.tabText( self.notebook.currentIndex()).strip() == _("Absorption"): text = get_ref_text(os.path.join(self.path, "alpha.ref")) if text == None: text = "" help_window().help_set_help( ["alpha.png", _("<big><b>Absorption</b></big><br>" + text)]) self.ribbon.tb_save.setEnabled(True) self.ribbon.import_data.setEnabled(True) if self.notebook.tabText( self.notebook.currentIndex()).strip() == _("Refractive index"): text = get_ref_text(os.path.join(self.path, "n.ref")) if text == None: text = "" help_window().help_set_help( ["n.png", _("<big><b>Refractive index</b></big><br>" + text)]) self.ribbon.tb_save.setEnabled(True) self.ribbon.import_data.setEnabled(True) def callback_cost(self): desktop_open(os.path.join(self.path, "cost.xlsx")) def callback_help(self): webbrowser.open("https://www.gpvdm.com/man/index.html") def __init__(self, path): QWidgetSavePos.__init__(self, "materials_main") self.path = path self.setFixedSize(900, 600) self.setWindowIcon(icon_get("organic_material")) self.setWindowTitle( _("Material editor") + " (https://www.gpvdm.com)" + " " + os.path.basename(self.path)) self.main_vbox = QVBoxLayout() self.ribbon = ribbon_materials() self.ribbon.cost.triggered.connect(self.callback_cost) self.ribbon.folder_open.triggered.connect(self.callback_dir_open) self.ribbon.import_data.triggered.connect(self.import_data) self.ribbon.tb_ref.triggered.connect(self.callback_ref) self.ribbon.help.triggered.connect(self.callback_help) self.main_vbox.addWidget(self.ribbon) self.notebook = QTabWidget() self.notebook.setMovable(True) self.main_vbox.addWidget(self.notebook) #alpha=equation(self.path,"alpha_eq.inp","alpha_gen.omat","alpha.omat","#mat_default_file_alpha") #alpha.set_default_value("1e7") #alpha.set_ylabel(_("Absorption")+" (m^{-1})") #alpha.init() fname = os.path.join(self.path, "alpha.omat") self.alpha = plot_widget() self.alpha.init(enable_toolbar=False) self.alpha.set_labels([_("Absorption")]) self.alpha.load_data([fname], os.path.splitext(fname)[0] + ".oplot") self.alpha.do_plot() self.notebook.addTab(self.alpha, _("Absorption")) fname = os.path.join(self.path, "n.omat") self.n = plot_widget() self.n.init(enable_toolbar=False) self.n.set_labels([_("Refractive index")]) self.n.load_data([fname], os.path.splitext(fname)[0] + ".oplot") self.n.do_plot() self.notebook.addTab(self.n, _("Refractive index")) files = ["dos.inp", "pl.inp", "mat.inp"] description = [ _("Electrical parameters"), _("Luminescence"), _("Basic") ] for i in range(0, len(files)): tab = tab_class() full_path = os.path.join(self.path, files[i]) if os.path.isfile(full_path) == True: tab.init(os.path.join(self.path, files[i]), description[i]) self.notebook.addTab(tab, description[i]) self.setLayout(self.main_vbox) self.notebook.currentChanged.connect(self.changed_click) def import_data(self): file_name = None if self.notebook.tabText( self.notebook.currentIndex()).strip() == _("Absorption"): file_name = "alpha.omat" if self.notebook.tabText( self.notebook.currentIndex()).strip() == _("Refractive index"): file_name = "n.omat" if file_name != None: output_file = os.path.join(self.path, file_name) config_file = os.path.join(self.path, file_name + "import.inp") self.im = import_data(output_file, config_file) self.im.run() self.update() def import_ref(self): file_name = None if self.notebook.tabText( self.notebook.currentIndex()).strip() == _("Absorption"): file_name = "alpha.omat" if self.notebook.tabText( self.notebook.currentIndex()).strip() == _("Refractive index"): file_name = "n.omat" if file_name != None: output_file = os.path.join(self.path, file_name) config_file = os.path.join(self.path, file_name + "import.inp") self.im = import_data(output_file, config_file) self.im.run() self.update() def update(self): self.n.update() self.alpha.update() def callback_ref(self): file_name = None if self.notebook.tabText( self.notebook.currentIndex()).strip() == _("Absorption"): file_name = "alpha.omat" if self.notebook.tabText( self.notebook.currentIndex()).strip() == _("Refractive index"): file_name = "n.omat" if file_name != None: self.ref_window = ref_window(os.path.join(self.path, file_name)) self.ref_window.show() def callback_dir_open(self): dialog = gpvdm_open(self.path) dialog.show_inp_files = False ret = dialog.exec_() if ret == QDialog.Accepted: desktop_open(dialog.get_filename())
class MainWindow(QMainWindow): def __init__(self, iris, settings, parent=None, **kwargs): QMainWindow.__init__(self, parent) self._splash = QSplashScreen(self, QPixmap('data/logo.tif')) self._splash.show() self.setAttribute(Qt.WA_DeleteOnClose) self.setWindowTitle( "Iris Control GUI - %s - Caution use at your own risk!" % kwargs['handle']['label']) self.setMinimumSize(800, 600) self._settings = settings #start the window self._controlTabs = QTabWidget(self) self.setCentralWidget(self._controlTabs) self._mainTab = HighLevelControlTab(iris, [0, 1], self._controlTabs) self._controlTabs.addTab(self._mainTab, "Main") for name, chans, start, stop in [ ('LML', [ 0, ], 0x0000, 0x002F), ('TxTSP', [0, 1], 0x0200, 0x020C), ('RxTSP', [0, 1], 0x0400, 0x040D), ('SXX', [0, 1], 0x011C, 0x0124), ('RFE', [0, 1], 0x010C, 0x0114), ('RBB', [0, 1], 0x0115, 0x011B), ('TRF', [0, 1], 0x0100, 0x0104), ('TBB', [0, 1], 0x0105, 0x010B), ('EN_DIR', [ 0, ], 0x0081, 0x0081), ('LDO', [ 0, ], 0x0092, 0x00A7), ('DC', [ 0, ], 0x05C0, 0x05CC), ('AFE', [ 0, ], 0x0082, 0x0082), ('BIAS', [ 0, ], 0x0083, 0x0084), ('XBUF', [ 0, ], 0x0085, 0x0085), ('CGEN', [ 0, ], 0x0086, 0x008D), ('RSSI', [ 0, ], 0x0600, 0x0641), ]: scroll = QScrollArea(self._controlTabs) tab = LowLevelControlTab(iris, chans, start, stop, scroll) scroll.setWidget(tab) self._controlTabs.addTab(scroll, name) #load previous settings print("Loading %s" % self._settings.fileName()) if self._settings.contains("MainWindow/geometry"): self.restoreGeometry(self._settings.value("MainWindow/geometry")) if self._settings.contains("MainWindow/state"): self.restoreState(self._settings.value("MainWindow/state")) if self._settings.contains("MainWindow/tab"): self._controlTabs.setCurrentIndex( int(self._settings.value("MainWindow/tab"))) #load complete self._splash.finish(self) def loadFile(self, filePath): self._mainTab.loadFile(filePath) def closeEvent(self, event): #stash settings self._settings.setValue("MainWindow/geometry", self.saveGeometry()) self._settings.setValue("MainWindow/state", self.saveState()) self._settings.setValue("MainWindow/tab", self._controlTabs.currentIndex())
class FramelessWindow(QWidget): # 四周边距 Margins = 2 # 窗口移动 windowMoved = pyqtSignal(QPoint) def __init__(self): super(FramelessWindow, self).__init__() self.tab_ = {} self.initTab() # 初始化一个 tab self.newTab() self._pressed = False self.Direction = None # 背景透明 self.setAttribute(Qt.WA_TranslucentBackground, True) # 无边框 self.setWindowFlags(Qt.FramelessWindowHint) # 隐藏边框 # 鼠标跟踪 self.setMouseTracking(True) # 布局 layout = QVBoxLayout(self, spacing=0) # 预留边界用于实现无边框窗口调整大小 layout.setContentsMargins(self.Margins, self.Margins, self.Margins, self.Margins) # 标题栏 self.titleBar = TitleBar(self) # layout = QGridLayout(self, spacing=0) # layout.setContentsMargins(0, 0, 0, 0) layout.addWidget(self.titleBar) # self.titleBar.layout.addChildWidget(self.tabWidget.tabBar()) # self.layout().addWidget(self.titleBar) self.titleBar.layout.addChildWidget(self.tabWidget.tabBar()) self.titleBar.loadFnBtn() # self.addPage = QAction(QIcon(':/icons/plus.png'), '页面缩小', self) # QTabBar.setS self.layout().addWidget(self.tabWidget) self.titleBar.addPaged.connect(self.newTab) self.statusBar = QStatusBar() layout.addWidget(self.statusBar) self.headStatusBarLabel = QLabel("就绪") self.headStatusBarLabel.setObjectName("headStatusBarLabel") self.statusBar.addWidget(self.headStatusBarLabel) # 网页加载标签 self.statusBarLabel = QLabel("网页加载") self.statusBarLabel.setObjectName("statusBarLabel") self.statusBar.addPermanentWidget(self.statusBarLabel) # 网页加载进度条 self.statusBarLabelProgress = QProgressBar(self) self.statusBar.addPermanentWidget(self.statusBarLabelProgress) self.statusBarLabelProgress.setObjectName("statusBarLabelProgress") self.statusBarLabelProgress.setFixedWidth(200) self.statusBarLabelProgress.setHidden(True) self.statusBarLabel.setHidden(True) # 文件下载标签 self.statusBarDownloadLabel = QLabel("") self.statusBarDownloadLabel.setObjectName("statusBarDownloadLabel") self.statusBar.addPermanentWidget(self.statusBarDownloadLabel) self.statusBarDownloadLabel.setHidden(True) self.statusBar.setHidden(True) self.statusBar.setCursor(Qt.ArrowCursor) # 信号槽 self.titleBar.windowMinimumed.connect(self.showMinimized) self.titleBar.windowMaximumed.connect(self.showMaximized) self.titleBar.windowNormaled.connect(self.showNormal) self.titleBar.windowClosed.connect(self.close) self.tabBar.windowMoved.connect(self.move) # self.titleBar.windowMoved.connect(self.move) # self.windowTitleChanged.connect(self.titleBar.setTitle) # self.windowIconChanged.connect(self.titleBar.setIcon) self.show() def initTab(self): # print("initTab") # self.setWindowFlags(Qt.FramelessWindowHint) # 去掉标题栏的代码 # 设置窗口标题 self.setWindowTitle('PyQt浏览器 v2.0') # 设置窗口图标 self.setWindowIcon(QIcon(':/icons/logo.png')) self.tabWidget = QTabWidget() # self.tabWidget.setTabBarAutoHide(True) self.tabBar = TabBarDe(self) # self.tabBar.addAction() self.tabWidget.setTabBar(self.tabBar) # self.tabWidget.setTabShape(QTabWidget.Triangular) # QTabWidget.Triangular self.tabWidget.setDocumentMode(True) self.tabWidget.setMovable(True) self.tabWidget.setTabsClosable(True) self.tabWidget.tabCloseRequested.connect(self.close_Tab) self.tabWidget.currentChanged.connect(self.changeTab) # self.tabWidget.tabBar().setAutoHide(True) height = win32api.GetSystemMetrics(win32con.SM_CXSCREEN) // 2 width = win32api.GetSystemMetrics(win32con.SM_CYSCREEN) // 2 self.setMinimumSize(QSize(height * 0.7, width * 0.7)) # self.center() # self.show() def newTab(self): # print("new tab") test = QPushButton("test") self.tab = QWidget() self.tabWidget.addTab(self.tab, "新标签页") self.tabWidget.setCurrentWidget(self.tab) index = self.tabWidget.currentIndex() try: self.tabWidget.setTabIcon(index, QIcon(":icons/earth_128.png")) except BaseException as base: print(base) ##### self.Layout = QVBoxLayout(self.tab) self.Layout.setContentsMargins(0, 0, 0, 0) self.browser = Browser(self) self.browser.webview.setObjectName(str(index)) self.tab.setObjectName("tab_" + str(index)) self.tab_[str(index)] = self.browser.webview self.Layout.addWidget(self.browser) # 关闭tab def close_Tab(self, index): if self.tabWidget.count() > 1: # self.browser.webview.page().url(QUrl("")) # print(self.tab_[str(index)]) webObj_index = str(self.tabWidget.widget(index).objectName()).split("_")[1] # self.tab_[webObj_index].page().setUrl(QUrl(NEW_PAGE)) # self.tab_[webObj_index].deleteLater() print(self.tab_[webObj_index].history()) self.tab_.pop(webObj_index) # print(self.tab_) self.tabWidget.removeTab(index) # print(index) # self.browser.on_windowCloseRequested() else: self.close() # 当只有1个tab时,关闭主窗口 def changeTab(self, index): # print("index:%d" % (index)) pass def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def setTitleBarHeight(self, height=34): """设置标题栏高度""" self.titleBar.setHeight(height) def setIconSize(self, size): """设置图标的大小""" self.titleBar.setIconSize(size) def setWidget(self, widget): """设置自己的控件""" if hasattr(self, '_widget'): return self._widget = widget # 设置默认背景颜色,否则由于受到父窗口的影响导致透明 self._widget.setAutoFillBackground(True) palette = self._widget.palette() palette.setColor(palette.Window, QColor(240, 240, 240)) self._widget.setPalette(palette) self._widget.installEventFilter(self) self.layout().addWidget(self._widget) def move(self, pos): if self.windowState() == Qt.WindowMaximized or self.windowState() == Qt.WindowFullScreen: # 最大化或者全屏则不允许移动 return super(FramelessWindow, self).move(pos) def showMaximized(self): """最大化,要去除上下左右边界,如果不去除则边框地方会有空隙""" super(FramelessWindow, self).showMaximized() self.layout().setContentsMargins(0, 0, 0, 0) def showFullScreen(self): super(FramelessWindow, self).showFullScreen() self.layout().setContentsMargins(0, 0, 0, 0) def showNormal(self): """还原,要保留上下左右边界,否则没有边框无法调整""" super(FramelessWindow, self).showNormal() self.layout().setContentsMargins( self.Margins, self.Margins, self.Margins, self.Margins) def eventFilter(self, obj, event): """事件过滤器,用于解决鼠标进入其它控件后还原为标准鼠标样式""" if isinstance(event, QEnterEvent): self.setCursor(Qt.ArrowCursor) return super(FramelessWindow, self).eventFilter(obj, event) def paintEvent(self, event): """由于是全透明背景窗口,重绘事件中绘制透明度为1的难以发现的边框,用于调整窗口大小""" super(FramelessWindow, self).paintEvent(event) painter = QPainter(self) painter.setPen(QPen(QColor(255, 255, 255, 1), 2 * self.Margins)) painter.drawRect(self.rect()) def mousePressEvent(self, event): """鼠标点击事件""" super(FramelessWindow, self).mousePressEvent(event) if event.button() == Qt.LeftButton: self._mpos = event.pos() self._pressed = True def mouseReleaseEvent(self, event): '''鼠标弹起事件''' super(FramelessWindow, self).mouseReleaseEvent(event) self._pressed = False self.Direction = None def mouseMoveEvent(self, event): """鼠标移动事件""" super(FramelessWindow, self).mouseMoveEvent(event) pos = event.pos() xPos, yPos = pos.x(), pos.y() wm, hm = self.width() - self.Margins, self.height() - self.Margins if self.isMaximized() or self.isFullScreen(): self.Direction = None self.setCursor(Qt.ArrowCursor) return if event.buttons() == Qt.LeftButton and self._pressed: self._resizeWidget(pos) return if xPos <= self.Margins and yPos <= self.Margins: # 左上角 self.Direction = LeftTop self.setCursor(Qt.SizeFDiagCursor) elif wm <= xPos <= self.width() and hm <= yPos <= self.height(): # 右下角 self.Direction = RightBottom self.setCursor(Qt.SizeFDiagCursor) elif wm <= xPos and yPos <= self.Margins: # 右上角 self.Direction = RightTop self.setCursor(Qt.SizeBDiagCursor) elif xPos <= self.Margins and hm <= yPos: # 左下角 self.Direction = LeftBottom self.setCursor(Qt.SizeBDiagCursor) elif 0 <= xPos <= self.Margins and self.Margins <= yPos <= hm: # 左边 self.Direction = Left self.setCursor(Qt.SizeHorCursor) elif wm <= xPos <= self.width() and self.Margins <= yPos <= hm: # 右边 self.Direction = Right self.setCursor(Qt.SizeHorCursor) elif self.Margins <= xPos <= wm and 0 <= yPos <= self.Margins: # 上面 self.Direction = Top self.setCursor(Qt.SizeVerCursor) elif self.Margins <= xPos <= wm and hm <= yPos <= self.height(): # 下面 self.Direction = Bottom self.setCursor(Qt.SizeVerCursor) def _resizeWidget(self, pos): """调整窗口大小""" if self.Direction == None: return mpos = pos - self._mpos xPos, yPos = mpos.x(), mpos.y() geometry = self.geometry() x, y, w, h = geometry.x(), geometry.y(), geometry.width(), geometry.height() if self.Direction == LeftTop: # 左上角 if w - xPos > self.minimumWidth(): x += xPos w -= xPos if h - yPos > self.minimumHeight(): y += yPos h -= yPos elif self.Direction == RightBottom: # 右下角 if w + xPos > self.minimumWidth(): w += xPos self._mpos = pos if h + yPos > self.minimumHeight(): h += yPos self._mpos = pos elif self.Direction == RightTop: # 右上角 if h - yPos > self.minimumHeight(): y += yPos h -= yPos if w + xPos > self.minimumWidth(): w += xPos self._mpos.setX(pos.x()) elif self.Direction == LeftBottom: # 左下角 if w - xPos > self.minimumWidth(): x += xPos w -= xPos if h + yPos > self.minimumHeight(): h += yPos self._mpos.setY(pos.y()) elif self.Direction == Left: # 左边 if w - xPos > self.minimumWidth(): x += xPos w -= xPos else: return elif self.Direction == Right: # 右边 if w + xPos > self.minimumWidth(): w += xPos self._mpos = pos else: return elif self.Direction == Top: # 上面 if h - yPos > self.minimumHeight(): y += yPos h -= yPos else: return elif self.Direction == Bottom: # 下面 if h + yPos > self.minimumHeight(): h += yPos self._mpos = pos else: return self.setGeometry(x, y, w, h)
class ReTextWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.resize(950, 700) screenRect = QDesktopWidget().screenGeometry() if globalSettings.windowGeometry: self.restoreGeometry(globalSettings.windowGeometry) else: self.move((screenRect.width()-self.width())/2, (screenRect.height()-self.height())/2) if not screenRect.contains(self.geometry()): self.showMaximized() if sys.platform.startswith('darwin'): # https://github.com/retext-project/retext/issues/198 searchPaths = QIcon.themeSearchPaths() searchPaths.append('/opt/local/share/icons') searchPaths.append('/usr/local/share/icons') QIcon.setThemeSearchPaths(searchPaths) if globalSettings.iconTheme: QIcon.setThemeName(globalSettings.iconTheme) if QIcon.themeName() in ('hicolor', ''): if not QFile.exists(icon_path + 'document-new.png'): QIcon.setThemeName(get_icon_theme()) if QFile.exists(icon_path+'retext.png'): self.setWindowIcon(QIcon(icon_path+'retext.png')) elif QFile.exists('/usr/share/pixmaps/retext.png'): self.setWindowIcon(QIcon('/usr/share/pixmaps/retext.png')) else: self.setWindowIcon(QIcon.fromTheme('retext', QIcon.fromTheme('accessories-text-editor'))) self.tabWidget = QTabWidget(self) self.initTabWidget() self.setCentralWidget(self.tabWidget) self.tabWidget.currentChanged.connect(self.changeIndex) self.tabWidget.tabCloseRequested.connect(self.closeTab) toolBar = QToolBar(self.tr('File toolbar'), self) self.addToolBar(Qt.TopToolBarArea, toolBar) self.editBar = QToolBar(self.tr('Edit toolbar'), self) self.addToolBar(Qt.TopToolBarArea, self.editBar) self.searchBar = QToolBar(self.tr('Search toolbar'), self) self.addToolBar(Qt.BottomToolBarArea, self.searchBar) toolBar.setVisible(not globalSettings.hideToolBar) self.editBar.setVisible(not globalSettings.hideToolBar) self.actionNew = self.act(self.tr('New'), 'document-new', self.createNew, shct=QKeySequence.New) self.actionNew.setPriority(QAction.LowPriority) self.actionOpen = self.act(self.tr('Open'), 'document-open', self.openFile, shct=QKeySequence.Open) self.actionOpen.setPriority(QAction.LowPriority) self.actionSetEncoding = self.act(self.tr('Set encoding'), trig=self.showEncodingDialog) self.actionSetEncoding.setEnabled(False) self.actionReload = self.act(self.tr('Reload'), 'view-refresh', lambda: self.currentTab.readTextFromFile()) self.actionReload.setEnabled(False) self.actionSave = self.act(self.tr('Save'), 'document-save', self.saveFile, shct=QKeySequence.Save) self.actionSave.setEnabled(False) self.actionSave.setPriority(QAction.LowPriority) self.actionSaveAs = self.act(self.tr('Save as'), 'document-save-as', self.saveFileAs, shct=QKeySequence.SaveAs) self.actionNextTab = self.act(self.tr('Next tab'), 'go-next', lambda: self.switchTab(1), shct=Qt.CTRL+Qt.Key_PageDown) self.actionPrevTab = self.act(self.tr('Previous tab'), 'go-previous', lambda: self.switchTab(-1), shct=Qt.CTRL+Qt.Key_PageUp) self.actionPrint = self.act(self.tr('Print'), 'document-print', self.printFile, shct=QKeySequence.Print) self.actionPrint.setPriority(QAction.LowPriority) self.actionPrintPreview = self.act(self.tr('Print preview'), 'document-print-preview', self.printPreview) self.actionViewHtml = self.act(self.tr('View HTML code'), 'text-html', self.viewHtml) self.actionChangeEditorFont = self.act(self.tr('Change editor font'), trig=self.changeEditorFont) self.actionChangePreviewFont = self.act(self.tr('Change preview font'), trig=self.changePreviewFont) self.actionSearch = self.act(self.tr('Find text'), 'edit-find', shct=QKeySequence.Find) self.actionSearch.setCheckable(True) self.actionSearch.triggered[bool].connect(self.searchBar.setVisible) self.searchBar.visibilityChanged.connect(self.searchBarVisibilityChanged) self.actionPreview = self.act(self.tr('Preview'), shct=Qt.CTRL+Qt.Key_E, trigbool=self.preview) if QIcon.hasThemeIcon('document-preview'): self.actionPreview.setIcon(QIcon.fromTheme('document-preview')) elif QIcon.hasThemeIcon('preview-file'): self.actionPreview.setIcon(QIcon.fromTheme('preview-file')) elif QIcon.hasThemeIcon('x-office-document'): self.actionPreview.setIcon(QIcon.fromTheme('x-office-document')) else: self.actionPreview.setIcon(QIcon(icon_path+'document-preview.png')) self.actionLivePreview = self.act(self.tr('Live preview'), shct=Qt.CTRL+Qt.Key_L, trigbool=self.enableLivePreview) menuPreview = QMenu() menuPreview.addAction(self.actionLivePreview) self.actionPreview.setMenu(menuPreview) self.actionTableMode = self.act(self.tr('Table editing mode'), shct=Qt.CTRL+Qt.Key_T, trigbool=lambda x: self.currentTab.editBox.enableTableMode(x)) if ReTextFakeVimHandler: self.actionFakeVimMode = self.act(self.tr('FakeVim mode'), shct=Qt.CTRL+Qt.ALT+Qt.Key_V, trigbool=self.enableFakeVimMode) if globalSettings.useFakeVim: self.actionFakeVimMode.setChecked(True) self.enableFakeVimMode(True) self.actionFullScreen = self.act(self.tr('Fullscreen mode'), 'view-fullscreen', shct=Qt.Key_F11, trigbool=self.enableFullScreen) self.actionFullScreen.setPriority(QAction.LowPriority) self.actionConfig = self.act(self.tr('Preferences'), icon='preferences-system', trig=self.openConfigDialog) self.actionConfig.setMenuRole(QAction.PreferencesRole) self.actionSaveHtml = self.act('HTML', 'text-html', self.saveFileHtml) self.actionPdf = self.act('PDF', 'application-pdf', self.savePdf) self.actionOdf = self.act('ODT', 'x-office-document', self.saveOdf) self.getExportExtensionsList() self.actionQuit = self.act(self.tr('Quit'), 'application-exit', shct=QKeySequence.Quit) self.actionQuit.setMenuRole(QAction.QuitRole) self.actionQuit.triggered.connect(self.close) self.actionUndo = self.act(self.tr('Undo'), 'edit-undo', lambda: self.currentTab.editBox.undo(), shct=QKeySequence.Undo) self.actionRedo = self.act(self.tr('Redo'), 'edit-redo', lambda: self.currentTab.editBox.redo(), shct=QKeySequence.Redo) self.actionCopy = self.act(self.tr('Copy'), 'edit-copy', lambda: self.currentTab.editBox.copy(), shct=QKeySequence.Copy) self.actionCut = self.act(self.tr('Cut'), 'edit-cut', lambda: self.currentTab.editBox.cut(), shct=QKeySequence.Cut) self.actionPaste = self.act(self.tr('Paste'), 'edit-paste', lambda: self.currentTab.editBox.paste(), shct=QKeySequence.Paste) self.actionUndo.setEnabled(False) self.actionRedo.setEnabled(False) self.actionCopy.setEnabled(False) self.actionCut.setEnabled(False) qApp = QApplication.instance() qApp.clipboard().dataChanged.connect(self.clipboardDataChanged) self.clipboardDataChanged() if enchant is not None: self.actionEnableSC = self.act(self.tr('Enable'), trigbool=self.enableSpellCheck) self.actionSetLocale = self.act(self.tr('Set locale'), trig=self.changeLocale) self.actionWebKit = self.act(self.tr('Use WebKit renderer'), trigbool=self.enableWebKit) if ReTextWebPreview is None: globalSettings.useWebKit = False self.actionWebKit.setEnabled(False) self.actionWebKit.setChecked(globalSettings.useWebKit) self.actionShow = self.act(self.tr('Show directory'), 'system-file-manager', self.showInDir) self.actionFind = self.act(self.tr('Next'), 'go-next', self.find, shct=QKeySequence.FindNext) self.actionFindPrev = self.act(self.tr('Previous'), 'go-previous', lambda: self.find(back=True), shct=QKeySequence.FindPrevious) self.actionReplace = self.act(self.tr('Replace'), 'edit-find-replace', lambda: self.find(replace=True)) self.actionReplaceAll = self.act(self.tr('Replace all'), trig=self.replaceAll) menuReplace = QMenu() menuReplace.addAction(self.actionReplaceAll) self.actionReplace.setMenu(menuReplace) self.actionCloseSearch = self.act(self.tr('Close'), 'window-close', lambda: self.searchBar.setVisible(False)) self.actionCloseSearch.setPriority(QAction.LowPriority) self.actionHelp = self.act(self.tr('Get help online'), 'help-contents', self.openHelp) self.aboutWindowTitle = self.tr('About ReText') self.actionAbout = self.act(self.aboutWindowTitle, 'help-about', self.aboutDialog) self.actionAbout.setMenuRole(QAction.AboutRole) self.actionAboutQt = self.act(self.tr('About Qt')) self.actionAboutQt.setMenuRole(QAction.AboutQtRole) self.actionAboutQt.triggered.connect(qApp.aboutQt) availableMarkups = markups.get_available_markups() if not availableMarkups: print('Warning: no markups are available!') if len(availableMarkups) > 1: self.chooseGroup = QActionGroup(self) markupActions = [] for markup in availableMarkups: markupAction = self.act(markup.name, trigbool=self.markupFunction(markup)) if markup.name == globalSettings.defaultMarkup: markupAction.setChecked(True) self.chooseGroup.addAction(markupAction) markupActions.append(markupAction) self.actionBold = self.act(self.tr('Bold'), shct=QKeySequence.Bold, trig=lambda: self.insertFormatting('bold')) self.actionItalic = self.act(self.tr('Italic'), shct=QKeySequence.Italic, trig=lambda: self.insertFormatting('italic')) self.actionUnderline = self.act(self.tr('Underline'), shct=QKeySequence.Underline, trig=lambda: self.insertFormatting('underline')) self.usefulTags = ('header', 'italic', 'bold', 'underline', 'numbering', 'bullets', 'image', 'link', 'inline code', 'code block', 'blockquote') self.usefulChars = ('deg', 'divide', 'dollar', 'hellip', 'laquo', 'larr', 'lsquo', 'mdash', 'middot', 'minus', 'nbsp', 'ndash', 'raquo', 'rarr', 'rsquo', 'times') self.formattingBox = QComboBox(self.editBar) self.formattingBox.addItem(self.tr('Formatting')) self.formattingBox.addItems(self.usefulTags) self.formattingBox.activated[str].connect(self.insertFormatting) self.symbolBox = QComboBox(self.editBar) self.symbolBox.addItem(self.tr('Symbols')) self.symbolBox.addItems(self.usefulChars) self.symbolBox.activated.connect(self.insertSymbol) self.updateStyleSheet() menubar = self.menuBar() menuFile = menubar.addMenu(self.tr('File')) menuEdit = menubar.addMenu(self.tr('Edit')) menuHelp = menubar.addMenu(self.tr('Help')) menuFile.addAction(self.actionNew) menuFile.addAction(self.actionOpen) self.menuRecentFiles = menuFile.addMenu(self.tr('Open recent')) self.menuRecentFiles.aboutToShow.connect(self.updateRecentFiles) menuFile.addAction(self.actionShow) menuFile.addAction(self.actionSetEncoding) menuFile.addAction(self.actionReload) menuFile.addSeparator() menuFile.addAction(self.actionSave) menuFile.addAction(self.actionSaveAs) menuFile.addSeparator() menuFile.addAction(self.actionNextTab) menuFile.addAction(self.actionPrevTab) menuFile.addSeparator() menuExport = menuFile.addMenu(self.tr('Export')) menuExport.addAction(self.actionSaveHtml) menuExport.addAction(self.actionOdf) menuExport.addAction(self.actionPdf) if self.extensionActions: menuExport.addSeparator() for action, mimetype in self.extensionActions: menuExport.addAction(action) menuExport.aboutToShow.connect(self.updateExtensionsVisibility) menuFile.addAction(self.actionPrint) menuFile.addAction(self.actionPrintPreview) menuFile.addSeparator() menuFile.addAction(self.actionQuit) menuEdit.addAction(self.actionUndo) menuEdit.addAction(self.actionRedo) menuEdit.addSeparator() menuEdit.addAction(self.actionCut) menuEdit.addAction(self.actionCopy) menuEdit.addAction(self.actionPaste) menuEdit.addSeparator() if enchant is not None: menuSC = menuEdit.addMenu(self.tr('Spell check')) menuSC.addAction(self.actionEnableSC) menuSC.addAction(self.actionSetLocale) menuEdit.addAction(self.actionSearch) menuEdit.addAction(self.actionChangeEditorFont) menuEdit.addAction(self.actionChangePreviewFont) menuEdit.addSeparator() if len(availableMarkups) > 1: self.menuMode = menuEdit.addMenu(self.tr('Default markup')) for markupAction in markupActions: self.menuMode.addAction(markupAction) menuFormat = menuEdit.addMenu(self.tr('Formatting')) menuFormat.addAction(self.actionBold) menuFormat.addAction(self.actionItalic) menuFormat.addAction(self.actionUnderline) menuEdit.addAction(self.actionWebKit) menuEdit.addSeparator() menuEdit.addAction(self.actionViewHtml) menuEdit.addAction(self.actionPreview) menuEdit.addAction(self.actionTableMode) if ReTextFakeVimHandler: menuEdit.addAction(self.actionFakeVimMode) menuEdit.addSeparator() menuEdit.addAction(self.actionFullScreen) menuEdit.addAction(self.actionConfig) menuHelp.addAction(self.actionHelp) menuHelp.addSeparator() menuHelp.addAction(self.actionAbout) menuHelp.addAction(self.actionAboutQt) toolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) toolBar.addAction(self.actionNew) toolBar.addSeparator() toolBar.addAction(self.actionOpen) toolBar.addAction(self.actionSave) toolBar.addAction(self.actionPrint) toolBar.addSeparator() toolBar.addAction(self.actionPreview) toolBar.addAction(self.actionFullScreen) self.editBar.addAction(self.actionUndo) self.editBar.addAction(self.actionRedo) self.editBar.addSeparator() self.editBar.addAction(self.actionCut) self.editBar.addAction(self.actionCopy) self.editBar.addAction(self.actionPaste) self.editBar.addSeparator() self.editBar.addWidget(self.formattingBox) self.editBar.addWidget(self.symbolBox) self.searchEdit = QLineEdit(self.searchBar) self.searchEdit.setPlaceholderText(self.tr('Search')) self.searchEdit.returnPressed.connect(self.find) self.replaceEdit = QLineEdit(self.searchBar) self.replaceEdit.setPlaceholderText(self.tr('Replace with')) self.replaceEdit.returnPressed.connect(self.find) self.csBox = QCheckBox(self.tr('Case sensitively'), self.searchBar) self.searchBar.addWidget(self.searchEdit) self.searchBar.addWidget(self.replaceEdit) self.searchBar.addSeparator() self.searchBar.addWidget(self.csBox) self.searchBar.addAction(self.actionFindPrev) self.searchBar.addAction(self.actionFind) self.searchBar.addAction(self.actionReplace) self.searchBar.addAction(self.actionCloseSearch) self.searchBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.searchBar.setVisible(False) self.autoSaveEnabled = globalSettings.autoSave if self.autoSaveEnabled: timer = QTimer(self) timer.start(60000) timer.timeout.connect(self.saveAll) self.ind = None if enchant is not None: self.sl = globalSettings.spellCheckLocale try: enchant.Dict(self.sl or None) except enchant.errors.Error as e: print(e, file=sys.stderr) globalSettings.spellCheck = False if globalSettings.spellCheck: self.actionEnableSC.setChecked(True) self.fileSystemWatcher = QFileSystemWatcher() self.fileSystemWatcher.fileChanged.connect(self.fileChanged) def restoreLastOpenedFiles(self): for file in readListFromSettings("lastFileList"): self.openFileWrapper(file) # Show the tab of last opened file lastTabIndex = globalSettings.lastTabIndex if lastTabIndex >= 0 and lastTabIndex < self.tabWidget.count(): self.tabWidget.setCurrentIndex(lastTabIndex) def iterateTabs(self): for i in range(self.tabWidget.count()): yield self.tabWidget.widget(i) def updateStyleSheet(self): if globalSettings.styleSheet: sheetfile = QFile(globalSettings.styleSheet) sheetfile.open(QIODevice.ReadOnly) self.ss = QTextStream(sheetfile).readAll() sheetfile.close() else: palette = QApplication.palette() self.ss = 'html { color: %s; }\n' % palette.color(QPalette.WindowText).name() self.ss += 'td, th { border: 1px solid #c3c3c3; padding: 0 3px 0 3px; }\n' self.ss += 'table { border-collapse: collapse; }\n' def initTabWidget(self): def dragEnterEvent(e): e.acceptProposedAction() def dropEvent(e): fn = bytes(e.mimeData().data('text/plain')).decode().rstrip() if fn.startswith('file:'): fn = QUrl(fn).toLocalFile() self.openFileWrapper(fn) self.tabWidget.setTabsClosable(True) self.tabWidget.setAcceptDrops(True) self.tabWidget.setMovable(True) self.tabWidget.dragEnterEvent = dragEnterEvent self.tabWidget.dropEvent = dropEvent if hasattr(self.tabWidget, 'setTabBarAutoHide'): self.tabWidget.setTabBarAutoHide(globalSettings.tabBarAutoHide) def act(self, name, icon=None, trig=None, trigbool=None, shct=None): if not isinstance(shct, QKeySequence): shct = QKeySequence(shct) if icon: action = QAction(self.actIcon(icon), name, self) else: action = QAction(name, self) if trig: action.triggered.connect(trig) elif trigbool: action.setCheckable(True) action.triggered[bool].connect(trigbool) if shct: action.setShortcut(shct) return action def actIcon(self, name): return QIcon.fromTheme(name, QIcon(icon_path+name+'.png')) def printError(self): import traceback print('Exception occurred while parsing document:', file=sys.stderr) traceback.print_exc() def tabFileNameChanged(self, tab): ''' Perform all UI state changes that need to be done when the filename of the current tab has changed. ''' if tab == self.currentTab: if tab.fileName: self.setWindowTitle("") self.setWindowFilePath(tab.fileName) self.tabWidget.setTabText(self.ind, tab.getBaseName()) self.tabWidget.setTabToolTip(self.ind, tab.fileName) QDir.setCurrent(QFileInfo(tab.fileName).dir().path()) else: self.setWindowFilePath('') self.setWindowTitle(self.tr('New document') + '[*]') canReload = bool(tab.fileName) and not self.autoSaveActive(tab) self.actionSetEncoding.setEnabled(canReload) self.actionReload.setEnabled(canReload) def tabActiveMarkupChanged(self, tab): ''' Perform all UI state changes that need to be done when the active markup class of the current tab has changed. ''' if tab == self.currentTab: markupClass = tab.getActiveMarkupClass() dtMarkdown = (markupClass == markups.MarkdownMarkup) dtMkdOrReST = dtMarkdown or (markupClass == markups.ReStructuredTextMarkup) self.formattingBox.setEnabled(dtMarkdown) self.symbolBox.setEnabled(dtMarkdown) self.actionUnderline.setEnabled(dtMarkdown) self.actionBold.setEnabled(dtMkdOrReST) self.actionItalic.setEnabled(dtMkdOrReST) def tabModificationStateChanged(self, tab): ''' Perform all UI state changes that need to be done when the modification state of the current tab has changed. ''' if tab == self.currentTab: changed = tab.editBox.document().isModified() if self.autoSaveActive(tab): changed = False self.actionSave.setEnabled(changed) self.setWindowModified(changed) def createTab(self, fileName): self.currentTab = ReTextTab(self, fileName, previewState=int(globalSettings.livePreviewByDefault)) self.currentTab.fileNameChanged.connect(lambda: self.tabFileNameChanged(self.currentTab)) self.currentTab.modificationStateChanged.connect(lambda: self.tabModificationStateChanged(self.currentTab)) self.currentTab.activeMarkupChanged.connect(lambda: self.tabActiveMarkupChanged(self.currentTab)) self.tabWidget.addTab(self.currentTab, self.tr("New document")) self.currentTab.updateBoxesVisibility() def closeTab(self, ind): if self.maybeSave(ind): if self.tabWidget.count() == 1: self.createTab("") closedTab = self.tabWidget.widget(ind) if closedTab.fileName: self.fileSystemWatcher.removePath(closedTab.fileName) self.tabWidget.removeTab(ind) closedTab.deleteLater() def changeIndex(self, ind): ''' This function is called when a different tab is selected. It changes the state of the window to mirror the current state of the newly selected tab. Future changes to this state will be done in response to signals emitted by the tab, to which the window was subscribed when the tab was created. The window is subscribed to all tabs like this, but only the active tab will logically generate these signals. Aside from the above this function also calls the handlers for the other changes that are implied by a tab switch: filename change, modification state change and active markup change. ''' self.currentTab = self.tabWidget.currentWidget() editBox = self.currentTab.editBox previewState = self.currentTab.previewState self.actionUndo.setEnabled(editBox.document().isUndoAvailable()) self.actionRedo.setEnabled(editBox.document().isRedoAvailable()) self.actionCopy.setEnabled(editBox.textCursor().hasSelection()) self.actionCut.setEnabled(editBox.textCursor().hasSelection()) self.actionPreview.setChecked(previewState >= PreviewLive) self.actionLivePreview.setChecked(previewState == PreviewLive) self.actionTableMode.setChecked(editBox.tableModeEnabled) self.editBar.setEnabled(previewState < PreviewNormal) self.ind = ind editBox.setFocus(Qt.OtherFocusReason) self.tabFileNameChanged(self.currentTab) self.tabModificationStateChanged(self.currentTab) self.tabActiveMarkupChanged(self.currentTab) def changeEditorFont(self): font, ok = QFontDialog.getFont(globalSettings.editorFont, self) if ok: globalSettings.editorFont = font for tab in self.iterateTabs(): tab.editBox.updateFont() def changePreviewFont(self): font, ok = QFontDialog.getFont(globalSettings.font, self) if ok: globalSettings.font = font for tab in self.iterateTabs(): tab.triggerPreviewUpdate() def preview(self, viewmode): self.currentTab.previewState = viewmode * 2 self.actionLivePreview.setChecked(False) self.editBar.setDisabled(viewmode) self.currentTab.updateBoxesVisibility() def enableLivePreview(self, livemode): self.currentTab.previewState = int(livemode) self.actionPreview.setChecked(livemode) self.editBar.setEnabled(True) self.currentTab.updateBoxesVisibility() def enableWebKit(self, enable): globalSettings.useWebKit = enable for tab in self.iterateTabs(): tab.previewBox.disconnectExternalSignals() tab.previewBox.setParent(None) tab.previewBox.deleteLater() tab.previewBox = tab.createPreviewBox(tab.editBox) tab.previewBox.setMinimumWidth(125) tab.addWidget(tab.previewBox) tab.setSizes((50, 50)) tab.triggerPreviewUpdate() tab.updateBoxesVisibility() def enableCopy(self, copymode): self.actionCopy.setEnabled(copymode) self.actionCut.setEnabled(copymode) def enableFullScreen(self, yes): if yes: self.showFullScreen() else: self.showNormal() def openConfigDialog(self): dlg = ConfigDialog(self) dlg.setWindowTitle(self.tr('Preferences')) dlg.show() def enableFakeVimMode(self, yes): globalSettings.useFakeVim = yes if yes: FakeVimMode.init(self) for tab in self.iterateTabs(): tab.editBox.installFakeVimHandler() else: FakeVimMode.exit(self) def enableSpellCheck(self, yes): try: dict = enchant.Dict(self.sl or None) except enchant.errors.Error as e: QMessageBox.warning(self, '', str(e)) self.actionEnableSC.setChecked(False) yes = False self.setAllDictionaries(dict if yes else None) globalSettings.spellCheck = yes def setAllDictionaries(self, dictionary): for tab in self.iterateTabs(): hl = tab.highlighter hl.dictionary = dictionary hl.rehighlight() def changeLocale(self): localedlg = LocaleDialog(self, defaultText=self.sl) if localedlg.exec() != QDialog.Accepted: return sl = localedlg.localeEdit.text() try: enchant.Dict(sl or None) except enchant.errors.Error as e: QMessageBox.warning(self, '', str(e)) else: self.sl = sl or None self.enableSpellCheck(self.actionEnableSC.isChecked()) if localedlg.checkBox.isChecked(): globalSettings.spellCheckLocale = sl def searchBarVisibilityChanged(self, visible): self.actionSearch.setChecked(visible) if visible: self.searchEdit.setFocus(Qt.ShortcutFocusReason) def find(self, back=False, replace=False): flags = QTextDocument.FindFlags() if back: flags |= QTextDocument.FindBackward if self.csBox.isChecked(): flags |= QTextDocument.FindCaseSensitively text = self.searchEdit.text() replaceText = self.replaceEdit.text() if replace else None found = self.currentTab.find(text, flags, replaceText=replaceText) self.setSearchEditColor(found) def replaceAll(self): text = self.searchEdit.text() replaceText = self.replaceEdit.text() found = self.currentTab.replaceAll(text, replaceText) self.setSearchEditColor(found) def setSearchEditColor(self, found): palette = self.searchEdit.palette() palette.setColor(QPalette.Active, QPalette.Base, Qt.white if found else QColor(255, 102, 102)) self.searchEdit.setPalette(palette) def showInDir(self): if self.currentTab.fileName: path = QFileInfo(self.currentTab.fileName).path() QDesktopServices.openUrl(QUrl.fromLocalFile(path)) else: QMessageBox.warning(self, '', self.tr("Please, save the file somewhere.")) def moveToTopOfRecentFileList(self, fileName): if fileName: files = readListFromSettings("recentFileList") if fileName in files: files.remove(fileName) files.insert(0, fileName) if len(files) > 10: del files[10:] writeListToSettings("recentFileList", files) def createNew(self, text=None): self.createTab("") self.ind = self.tabWidget.count()-1 self.tabWidget.setCurrentIndex(self.ind) if text: self.currentTab.editBox.textCursor().insertText(text) def switchTab(self, shift=1): self.tabWidget.setCurrentIndex((self.ind + shift) % self.tabWidget.count()) def updateRecentFiles(self): self.menuRecentFiles.clear() self.recentFilesActions = [] filesOld = readListFromSettings("recentFileList") files = [] for f in filesOld: if QFile.exists(f): files.append(f) self.recentFilesActions.append(self.act(f, trig=self.openFunction(f))) writeListToSettings("recentFileList", files) for action in self.recentFilesActions: self.menuRecentFiles.addAction(action) def markupFunction(self, markup): return lambda: self.setDefaultMarkup(markup) def openFunction(self, fileName): return lambda: self.openFileWrapper(fileName) def extensionFunction(self, data): return lambda: \ self.runExtensionCommand(data['Exec'], data['FileFilter'], data['DefaultExtension']) def getExportExtensionsList(self): extensions = [] for extsprefix in datadirs: extsdir = QDir(extsprefix+'/export-extensions/') if extsdir.exists(): for fileInfo in extsdir.entryInfoList(['*.desktop', '*.ini'], QDir.Files | QDir.Readable): extensions.append(self.readExtension(fileInfo.filePath())) locale = QLocale.system().name() self.extensionActions = [] for extension in extensions: try: if ('Name[%s]' % locale) in extension: name = extension['Name[%s]' % locale] elif ('Name[%s]' % locale.split('_')[0]) in extension: name = extension['Name[%s]' % locale.split('_')[0]] else: name = extension['Name'] data = {} for prop in ('FileFilter', 'DefaultExtension', 'Exec'): if 'X-ReText-'+prop in extension: data[prop] = extension['X-ReText-'+prop] elif prop in extension: data[prop] = extension[prop] else: data[prop] = '' action = self.act(name, trig=self.extensionFunction(data)) if 'Icon' in extension: action.setIcon(self.actIcon(extension['Icon'])) mimetype = extension['MimeType'] if 'MimeType' in extension else None except KeyError: print('Failed to parse extension: Name is required', file=sys.stderr) else: self.extensionActions.append((action, mimetype)) def updateExtensionsVisibility(self): markupClass = self.currentTab.getActiveMarkupClass() for action in self.extensionActions: if markupClass is None: action[0].setEnabled(False) continue mimetype = action[1] if mimetype is None: enabled = True elif markupClass == markups.MarkdownMarkup: enabled = (mimetype in ("text/x-retext-markdown", "text/x-markdown", "text/markdown")) elif markupClass == markups.ReStructuredTextMarkup: enabled = (mimetype in ("text/x-retext-rst", "text/x-rst")) else: enabled = False action[0].setEnabled(enabled) def readExtension(self, fileName): extFile = QFile(fileName) extFile.open(QIODevice.ReadOnly) extension = {} stream = QTextStream(extFile) while not stream.atEnd(): line = stream.readLine() if '=' in line: index = line.index('=') extension[line[:index].rstrip()] = line[index+1:].lstrip() extFile.close() return extension def openFile(self): supportedExtensions = ['.txt'] for markup in markups.get_all_markups(): supportedExtensions += markup.file_extensions fileFilter = ' (' + str.join(' ', ['*'+ext for ext in supportedExtensions]) + ');;' fileNames = QFileDialog.getOpenFileNames(self, self.tr("Select one or several files to open"), "", self.tr("Supported files") + fileFilter + self.tr("All files (*)")) for fileName in fileNames[0]: self.openFileWrapper(fileName) def openFileWrapper(self, fileName): if not fileName: return fileName = QFileInfo(fileName).canonicalFilePath() exists = False for i, tab in enumerate(self.iterateTabs()): if tab.fileName == fileName: exists = True ex = i if exists: self.tabWidget.setCurrentIndex(ex) elif QFile.exists(fileName): noEmptyTab = ( (self.ind is None) or self.currentTab.fileName or self.currentTab.editBox.toPlainText() or self.currentTab.editBox.document().isModified() ) if noEmptyTab: self.createTab(fileName) self.ind = self.tabWidget.count()-1 self.tabWidget.setCurrentIndex(self.ind) if fileName: self.fileSystemWatcher.addPath(fileName) self.currentTab.readTextFromFile(fileName) self.moveToTopOfRecentFileList(self.currentTab.fileName) def showEncodingDialog(self): if not self.maybeSave(self.ind): return codecsSet = set(bytes(QTextCodec.codecForName(alias).name()) for alias in QTextCodec.availableCodecs()) encoding, ok = QInputDialog.getItem(self, '', self.tr('Select file encoding from the list:'), [bytes(b).decode() for b in sorted(codecsSet)], 0, False) if ok: self.currentTab.readTextFromFile(None, encoding) def saveFileAs(self): self.saveFile(dlg=True) def saveAll(self): for tab in self.iterateTabs(): if tab.fileName and QFileInfo(tab.fileName).isWritable(): tab.saveTextToFile() def saveFile(self, dlg=False): fileNameToSave = self.currentTab.fileName if (not fileNameToSave) or dlg: markupClass = self.currentTab.getActiveMarkupClass() if (markupClass is None) or not hasattr(markupClass, 'default_extension'): defaultExt = self.tr("Plain text (*.txt)") ext = ".txt" else: defaultExt = self.tr('%s files', 'Example of final string: Markdown files') \ % markupClass.name + ' (' + str.join(' ', ('*'+extension for extension in markupClass.file_extensions)) + ')' if markupClass == markups.MarkdownMarkup: ext = globalSettings.markdownDefaultFileExtension elif markupClass == markups.ReStructuredTextMarkup: ext = globalSettings.restDefaultFileExtension else: ext = markupClass.default_extension fileNameToSave = QFileDialog.getSaveFileName(self, self.tr("Save file"), "", defaultExt)[0] if fileNameToSave: if not QFileInfo(fileNameToSave).suffix(): fileNameToSave += ext # Make sure we don't overwrite a file opened in other tab for tab in self.iterateTabs(): if tab is not self.currentTab and tab.fileName == fileNameToSave: QMessageBox.warning(self, "", self.tr("Cannot save to file which is open in another tab!")) return False self.actionSetEncoding.setDisabled(self.autoSaveActive()) if fileNameToSave: if self.currentTab.saveTextToFile(fileNameToSave): self.moveToTopOfRecentFileList(self.currentTab.fileName) return True else: QMessageBox.warning(self, '', self.tr("Cannot save to file because it is read-only!")) return False def saveHtml(self, fileName): if not QFileInfo(fileName).suffix(): fileName += ".html" try: _, htmltext, _ = self.currentTab.getDocumentForExport(includeStyleSheet=False, webenv=True) except Exception: return self.printError() htmlFile = QFile(fileName) htmlFile.open(QIODevice.WriteOnly) html = QTextStream(htmlFile) if globalSettings.defaultCodec: html.setCodec(globalSettings.defaultCodec) html << htmltext htmlFile.close() def textDocument(self, title, htmltext): td = QTextDocument() td.setMetaInformation(QTextDocument.DocumentTitle, title) if self.ss: td.setDefaultStyleSheet(self.ss) td.setHtml(htmltext) td.setDefaultFont(globalSettings.font) return td def saveOdf(self): title, htmltext, _ = self.currentTab.getDocumentForExport(includeStyleSheet=True, webenv=False) try: document = self.textDocument(title, htmltext) except Exception: return self.printError() fileName = QFileDialog.getSaveFileName(self, self.tr("Export document to ODT"), "", self.tr("OpenDocument text files (*.odt)"))[0] if not QFileInfo(fileName).suffix(): fileName += ".odt" writer = QTextDocumentWriter(fileName) writer.setFormat(b"odf") writer.write(document) def saveFileHtml(self): fileName = QFileDialog.getSaveFileName(self, self.tr("Save file"), "", self.tr("HTML files (*.html *.htm)"))[0] if fileName: self.saveHtml(fileName) def getDocumentForPrint(self, title, htmltext, preview): if globalSettings.useWebKit: return preview try: return self.textDocument(title, htmltext) except Exception: self.printError() def standardPrinter(self, title): printer = QPrinter(QPrinter.HighResolution) printer.setDocName(title) printer.setCreator('ReText %s' % app_version) return printer def savePdf(self): fileName = QFileDialog.getSaveFileName(self, self.tr("Export document to PDF"), "", self.tr("PDF files (*.pdf)"))[0] if fileName: if not QFileInfo(fileName).suffix(): fileName += ".pdf" title, htmltext, preview = self.currentTab.getDocumentForExport(includeStyleSheet=True, webenv=False) printer = self.standardPrinter(title) printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(fileName) document = self.getDocumentForPrint(title, htmltext, preview) if document != None: document.print(printer) def printFile(self): title, htmltext, preview = self.currentTab.getDocumentForExport(includeStyleSheet=True, webenv=False) printer = self.standardPrinter(title) dlg = QPrintDialog(printer, self) dlg.setWindowTitle(self.tr("Print document")) if (dlg.exec() == QDialog.Accepted): document = self.getDocumentForPrint(title, htmltext, preview) if document != None: document.print(printer) def printPreview(self): title, htmltext, preview = self.currentTab.getDocumentForExport(includeStyleSheet=True, webenv=False) document = self.getDocumentForPrint(title, htmltext, preview) if document is None: return printer = self.standardPrinter(title) preview = QPrintPreviewDialog(printer, self) preview.paintRequested.connect(document.print) preview.exec() def runExtensionCommand(self, command, filefilter, defaultext): import shlex of = ('%of' in command) html = ('%html' in command) if of: if defaultext and not filefilter: filefilter = '*'+defaultext fileName = QFileDialog.getSaveFileName(self, self.tr('Export document'), '', filefilter)[0] if not fileName: return if defaultext and not QFileInfo(fileName).suffix(): fileName += defaultext else: fileName = 'out' + defaultext basename = '.%s.retext-temp' % self.currentTab.getBaseName() if html: tmpname = basename+'.html' self.saveHtml(tmpname) else: tmpname = basename + self.currentTab.getActiveMarkupClass().default_extension self.currentTab.writeTextToFile(tmpname) command = command.replace('%of', shlex.quote(fileName)) command = command.replace('%html' if html else '%if', shlex.quote(tmpname)) try: Popen(str(command), shell=True).wait() except Exception as error: errorstr = str(error) QMessageBox.warning(self, '', self.tr('Failed to execute the command:') + '\n' + errorstr) QFile(tmpname).remove() def autoSaveActive(self, tab=None): tab = tab if tab else self.currentTab return bool(self.autoSaveEnabled and tab.fileName and QFileInfo(tab.fileName).isWritable()) def clipboardDataChanged(self): mimeData = QApplication.instance().clipboard().mimeData() if mimeData is not None: self.actionPaste.setEnabled(mimeData.hasText() or mimeData.hasImage()) def insertFormatting(self, formatting): cursor = self.currentTab.editBox.textCursor() text = cursor.selectedText() moveCursorTo = None def c(cursor): nonlocal moveCursorTo moveCursorTo = cursor.position() def ensurenl(cursor): if not cursor.atBlockStart(): cursor.insertText('\n\n') toinsert = { 'header': (ensurenl, '# ', text), 'italic': ('*', text, c, '*'), 'bold': ('**', text, c, '**'), 'underline': ('<u>', text, c, '</u>'), 'numbering': (ensurenl, ' 1. ', text), 'bullets': (ensurenl, ' * ', text), 'image': ('![', text or self.tr('Alt text'), c, '](', self.tr('URL'), ')'), 'link': ('[', text or self.tr('Link text'), c, '](', self.tr('URL'), ')'), 'inline code': ('`', text, c, '`'), 'code block': (ensurenl, ' ', text), 'blockquote': (ensurenl, '> ', text), } if formatting not in toinsert: return cursor.beginEditBlock() for token in toinsert[formatting]: if callable(token): token(cursor) else: cursor.insertText(token) cursor.endEditBlock() self.formattingBox.setCurrentIndex(0) # Bring back the focus on the editor self.currentTab.editBox.setFocus(Qt.OtherFocusReason) if moveCursorTo: cursor.setPosition(moveCursorTo) self.currentTab.editBox.setTextCursor(cursor) def insertSymbol(self, num): if num: self.currentTab.editBox.insertPlainText('&'+self.usefulChars[num-1]+';') self.symbolBox.setCurrentIndex(0) def fileChanged(self, fileName): ind = None for testind, tab in enumerate(self.iterateTabs()): if tab.fileName == fileName: ind = testind if ind is None: self.fileSystemWatcher.removePath(fileName) self.tabWidget.setCurrentIndex(ind) if not QFile.exists(fileName): self.currentTab.editBox.document().setModified(True) QMessageBox.warning(self, '', self.tr( 'This file has been deleted by other application.\n' 'Please make sure you save the file before exit.')) elif not self.currentTab.editBox.document().isModified(): # File was not modified in ReText, reload silently self.currentTab.readTextFromFile() else: text = self.tr( 'This document has been modified by other application.\n' 'Do you want to reload the file (this will discard all ' 'your changes)?\n') if self.autoSaveEnabled: text += self.tr( 'If you choose to not reload the file, auto save mode will ' 'be disabled for this session to prevent data loss.') messageBox = QMessageBox(QMessageBox.Warning, '', text) reloadButton = messageBox.addButton(self.tr('Reload'), QMessageBox.YesRole) messageBox.addButton(QMessageBox.Cancel) messageBox.exec() if messageBox.clickedButton() is reloadButton: self.currentTab.readTextFromFile() else: self.autoSaveEnabled = False self.currentTab.editBox.document().setModified(True) if fileName not in self.fileSystemWatcher.files(): # https://github.com/retext-project/retext/issues/137 self.fileSystemWatcher.addPath(fileName) def maybeSave(self, ind): tab = self.tabWidget.widget(ind) if self.autoSaveActive(tab): tab.saveTextToFile() return True if not tab.editBox.document().isModified(): return True self.tabWidget.setCurrentIndex(ind) ret = QMessageBox.warning(self, '', self.tr("The document has been modified.\nDo you want to save your changes?"), QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) if ret == QMessageBox.Save: return self.saveFile(False) elif ret == QMessageBox.Cancel: return False return True def closeEvent(self, closeevent): for ind in range(self.tabWidget.count()): if not self.maybeSave(ind): return closeevent.ignore() if globalSettings.saveWindowGeometry and not self.isMaximized(): globalSettings.windowGeometry = self.saveGeometry() if globalSettings.openLastFilesOnStartup: files = [tab.fileName for tab in self.iterateTabs()] writeListToSettings("lastFileList", files) globalSettings.lastTabIndex = self.tabWidget.currentIndex() closeevent.accept() def viewHtml(self): htmlDlg = HtmlDialog(self) try: _, htmltext, _ = self.currentTab.getDocumentForExport(includeStyleSheet=False, webenv=False) except Exception: return self.printError() winTitle = self.currentTab.getBaseName() htmlDlg.setWindowTitle(winTitle+" ("+self.tr("HTML code")+")") htmlDlg.textEdit.setPlainText(htmltext.rstrip()) htmlDlg.hl.rehighlight() htmlDlg.show() htmlDlg.raise_() htmlDlg.activateWindow() def openHelp(self): QDesktopServices.openUrl(QUrl('https://github.com/retext-project/retext/wiki')) def aboutDialog(self): QMessageBox.about(self, self.aboutWindowTitle, '<p><b>' + (self.tr('ReText %s (using PyMarkups %s)') % (app_version, markups.__version__)) +'</b></p>' + self.tr('Simple but powerful editor' ' for Markdown and reStructuredText') +'</p><p>'+self.tr('Author: Dmitry Shachnev, 2011').replace('2011', '2011–2016') +'<br><a href="https://github.com/retext-project/retext">'+self.tr('Website') +'</a> | <a href="http://daringfireball.net/projects/markdown/syntax">' +self.tr('Markdown syntax') +'</a> | <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">' +self.tr('reStructuredText syntax')+'</a></p>') def setDefaultMarkup(self, markupClass): globalSettings.defaultMarkup = markupClass.name for tab in self.iterateTabs(): if not tab.fileName: tab.updateActiveMarkupClass()
class ConfigurationPanel(QMainWindow): """docstring for ConfigurationPanel""" def __init__(self): super(ConfigurationPanel, self).__init__() self.bands = ["6m", "10m", "12m", "15m", "17m", "20m", "30m", "40m", "60m", "80m", "160m"] self.nrow = 16 self.nrele = 24 # creo le azioni che usero' in seguito self.initAction() self.initUI() def initUI(self): # imposto la grandezza del font self.setStyleSheet('font-size: 11pt;') # imposto le dimensioni della finestra self.setGeometry(0, 0, 1500, 600) # imposto il titolo della finestra self.setWindowTitle("AMC Configuration Panel") # creo il menu' self.initMenuBar() self.initToolBar() # creo le tab ed il loro contenuto self.initTab() # imposta il widget dato come widget centrale della finestra principale self.setCentralWidget(self.tab_widget) # sposta la finestra al centro del desktop self.centerOnScreen() # visualizzo il widget self.show() # visualizzo la scritta 'Ready' nella status bar self.statusBar().showMessage("Ready") def initAction(self): # TODO: sistemare il path delle icone # azione per uscire dall'applicazione self.exitAction = QAction( QIcon(os.path.join("icons", "exit.png")), "&Exit", self) self.exitAction.setShortcut("Ctrl+Q") self.exitAction.setStatusTip("Exit application") # collego l'azione exitAction all'evento self.close (gia' presente in PyQT) self.exitAction.triggered.connect(self.close) # azione per caricare la configurazione da file self.openAction = QAction( QIcon(os.path.join("icons", "open-configuration.png")), "&Load configuration", self) self.openAction.setShortcut("Ctrl+L") self.openAction.setStatusTip("Load to file") # collego l'azione openAction al mio evento che ho creato loadConfigurationFile self.openAction.triggered.connect(self.loadConfigurationFile) # azione per salvare la configurazione su file self.saveAction = QAction( QIcon(os.path.join("icons", "save-configuration.png")), "&Save configuration", self) self.saveAction.setShortcut("Ctrl+S") self.saveAction.setStatusTip("Save to file") self.saveAction.triggered.connect(self.saveConfigurationFile) # azione per spedire la configurazione al beaglebone self.uploadAction = QAction( QIcon(os.path.join("icons", "upload-configuration.png")), "&Upload configuration", self) self.uploadAction.setShortcut("Ctrl+U") self.uploadAction.setStatusTip("Upload to beaglebone") self.uploadAction.triggered.connect(self.uploadConfiguration) # azione per ricevere la configurazione dal beaglebone self.downloadAction = QAction( QIcon(os.path.join("icons", "download-configuration.png")), "&Download configuration", self) self.downloadAction.setShortcut("Ctrl+D") self.downloadAction.setStatusTip("Download from beaglebone") self.downloadAction.triggered.connect(self.downloadConfiguration) """ serialAction = QAction("&Serial", self) serialAction.setShortcut("") serialAction.setStatusTip("") serialAction.triggered.connect() downloadAction = QAction("&Download", self) downloadAction.setShortcut("") downloadAction.setStatusTip("") downloadAction.triggered.connect() UploadAction = QAction("&Upload", self) UploadAction.setShortcut("") UploadAction.setStatusTip("") UploadAction.triggered.connect() """ def initMenuBar(self): # creo il menu ed aggiungo i vari campi menubar = self.menuBar() filemenu = menubar.addMenu("&File") filemenu.addAction(self.openAction) filemenu.addAction(self.saveAction) filemenu.addSeparator() filemenu.addAction(self.downloadAction) filemenu.addAction(self.uploadAction) filemenu.addSeparator() # aggancio al menu il comando per uscire filemenu.addAction(self.exitAction) #configuration_menu = menubar.addMenu("&Configuration") #configuration_menu.addAction(self.saveAction) #configuration_menu.addSeparator() #configuration_menu.addAction(self.openAction) #connection_menu = menubar.addMenu("&Connection") def initToolBar(self): toolbar = self.addToolBar('ToolBar') toolbar.addAction(self.saveAction) toolbar.addSeparator() toolbar.addAction(self.openAction) toolbar.addSeparator() toolbar.addAction(self.downloadAction) toolbar.addSeparator() toolbar.addAction(self.uploadAction) def initTab(self): # creo un TabWidget che sara' il widget padre self.tab_widget = QTabWidget(self) labels_name = ["Active", "Label", "Bands"] tab_list = list() self.tablayout_list = list() self.checkbox_matrix = list() self.labels_matrix = list() self.radio1cb_matrix = list() self.radio2cb_matrix = list() for i in range(len(self.bands)): # aggiungo alla lista un oggetto QWidget discendente dal padre tab_list.append(QWidget(self.tab_widget)) # per ogni nome band aggiungo una tab nella list che # sara' visualizzata nella finestra principale self.tab_widget.addTab(tab_list[i], self.bands[i]) # aggiungo alla lista un oggetto per il layout a griglia discendente # dalla i-esima tab self.tablayout_list.append(QGridLayout(tab_list[i])) self.tablayout_list[i].setObjectName("tab" + str(i).zfill(2)) #print (self.tablayout_list[i].objectName()) # nella i-esima tab creo un layout a griglia e posiziono i vari elementi boldfont = QFont() boldfont.setBold(True) active_lbl = QLabel('Active:', tab_list[i]) active_lbl.setFont(boldfont) label_lbl = QLabel('Label:', tab_list[i]) label_lbl.setFont(boldfont) radio1_lbl = QLabel('Radio 1:', tab_list[i]) radio1_lbl.setFont(boldfont) radio2_lbl = QLabel('Radio 2:', tab_list[i]) radio2_lbl.setFont(boldfont) self.tablayout_list[i].addWidget(active_lbl, 0, 0) #self.tablayout_list[i].addWidget(QLabel("Label", tab_list[i]), 0, 1) self.tablayout_list[i].addWidget(label_lbl, 0, 1) #self.tablayout_list[i].addWidget(QLabel("Radio 1", tab_list[i]), 0, 2, 1, self.nrele) self.tablayout_list[i].addWidget(radio1_lbl, 0, 2, 1, self.nrele) #self.tablayout_list[i].addWidget(QLabel("Radio 2", tab_list[i]), 0, 26, 1, self.nrele) self.tablayout_list[i].addWidget(radio2_lbl, 0, 26, 1, self.nrele) self.checkbox_matrix.append(list()) self.labels_matrix.append(list()) self.radio1cb_matrix.append(list()) self.radio2cb_matrix.append(list()) # per ogni tab devo creare tutti i suoi oggetti al suo interno for y in range(self.nrow): self.checkbox_matrix[i].append(QCheckBox(tab_list[i])) self.checkbox_matrix[i][y].setObjectName("checkboxRowTab" + str(i).zfill(2)) self.checkbox_matrix[i][y].setCheckState(Qt.Unchecked) self.checkbox_matrix[i][y].stateChanged.connect(self.changeStateRow) #self.checkbox_matrix[i][y].setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding) self.tablayout_list[i].addWidget(self.checkbox_matrix[i][y], y+1, 0) self.labels_matrix[i].append(QLineEdit(tab_list[i])) self.labels_matrix[i][y].setEnabled(False) self.tablayout_list[i].addWidget(self.labels_matrix[i][y], y+1, 1) self.radio1cb_matrix[i].append(list()) self.radio2cb_matrix[i].append(list()) for z in range(self.nrele): self.radio1cb_matrix[i][y].append(QCheckBox(tab_list[i])) #self.radio1cb_matrix[i][y][z].setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding) #self.radio1cb_matrix[i][y][z].stateChanged.connect(self.changeCheckBoxState) self.radio1cb_matrix[i][y][z].setEnabled(False) # aggiungo 1 a y perche' devo contare le etichette (Active, Label, Radio1, Radio2) # aggiungo 2 a z perche' devo tenere conto dei widget di sinistra (checkbox e label) self.tablayout_list[i].addWidget(self.radio1cb_matrix[i][y][z], y+1, z+2) self.radio2cb_matrix[i][y].append(QCheckBox(tab_list[i])) #self.radio2cb_matrix[i][y][z].stateChanged.connect(self.changeCheckBoxState) self.radio2cb_matrix[i][y][z].setEnabled(False) # aggiungo 1 a y perche' devo contare le etichette (Active, Label, Radio1, Radio2) # aggiungo 2 a z perche' devo tenere conto dei widget di sinistra (checkbox, label e 24 checkbox) self.tablayout_list[i].addWidget(self.radio2cb_matrix[i][y][z], y+1, z+26) def centerOnScreen(self): ''' Centers the window on the screen. ''' # prende le dimensioni della finestra qr = self.frameGeometry() # prende il punto centrale del display date le sue dimensioni cp = QDesktopWidget().availableGeometry().center() # muoviamo la finestra al centro dello schermo qr.moveCenter(cp) self.move(qr.topLeft()) def warningOpenFile(self): QMessageBox.warning(self, 'Errore', "Errore nell'apertura del file") def closeEvent(self, event): reply = QMessageBox.question(self, 'Message', "Are you sure to quit?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No) if reply == QMessageBox.Yes: event.accept() else: event.ignore() def keyPressEvent(self, e): if e.key() == Qt.Key_Escape: self.close() def changeStateRow(self, state): if state == Qt.Checked or state == Qt.Unchecked: sender = self.sender() ''' Trovare un modo migliore per trovare la posizione dell'oggetto che effettua la richiesta perche' questo fa schifo!!!! ''' #print ("Sender: ", sender.objectName()) # Recupero l'indice della tab dove si trova la checkbox attraverso il nome indexTab = int(sender.objectName()[-2:]) #print ("indexTab: ", indexTab) indexPos = self.tablayout_list[indexTab].indexOf(sender) #print ("indexPos:", indexPos) location = self.tablayout_list[indexTab].getItemPosition(indexPos) indexRow = location[0] indexColumn = location[1] text = "Tab: " + self.bands[indexTab] + ", Row: " + str(indexColumn) + \ ", Column: " + str(indexRow) + ", State: " + str(state) #print (text) self.statusBar().showMessage(text) if state == Qt.Checked: self.labels_matrix[indexTab][indexRow-1].setEnabled(True) for i in range(self.nrele): # -1 perche' e' compresa la riga delle etichette self.radio1cb_matrix[indexTab][indexRow-1][i].setEnabled(True) self.radio2cb_matrix[indexTab][indexRow-1][i].setEnabled(True) else: self.labels_matrix[indexTab][indexRow-1].setEnabled(False) for i in range(self.nrele): self.radio1cb_matrix[indexTab][indexRow-1][i].setEnabled(False) self.radio2cb_matrix[indexTab][indexRow-1][i].setEnabled(False) def changeCheckBoxState(self, state): ''' Funzione che stampa a terminale tutte le volte che viene cliccata una checkbox ''' if state == Qt.Checked or state == Qt.Unchecked: #if True: sender = self.sender() #self.statusBar().showMessage(sender.text()) indexTab = self.tab_widget.currentIndex() """ button = self.sender() idx = self.layout.indexOf(button) location = self.layout.getItemPosition(idx) print ("Button", button, "at row/col", location[:2]) """ idx = self.tablayout_list[indexTab].indexOf(sender) location = self.tablayout_list[indexTab].getItemPosition(idx) indexRow = location[0] indexColumn = location[1] ''' print ("indexTab: ", indexTab) print ("idx:", idx) text = "Tab: " + str(indexTab) + ", Row: " + str(indexColumn) + \ ", Column: " + str(indexRow) + ", State: " + str(state) print (text) self.statusBar().showMessage(text) ''' #trovo la banda band = self.bands[indexTab] presetnumber = indexRow if indexColumn > 1 and indexColumn < 26: ptype = "radioA, relay " + str(location[1]-1) else: # -25 perche' sarebbe -26 (numero oggetti) + 1 (perche' non voglio partire da 0 ma da 1) ptype = "radioB, relay " + str(location[1]-25) # attenzione alla "label" print ("Posizione decodificata: banda ", band, ", preset numero ", presetnumber, ", tipo ", ptype) def createJsonConfiguration(self): jsonconfig = '{"relayconfig":{\n' commaTab = False for indexTab in range(len(self.bands)): firstRow = True nRowChecked = 0 for indexRow in range(self.nrow): if self.checkbox_matrix[indexTab][indexRow].isChecked() == True: nRowChecked += 1 if firstRow: if commaTab: # inserisco una virgola per ogni tab a parte l'ultima jsonconfig += ',\n' commaTab = False jsonconfig += '\t"' + self.bands[indexTab] + '":{\n' firstRow = False commaTab = True else: jsonconfig += ',\n' jsonconfig += '\t\t"' + str(indexRow) + '":{\n\t\t\t"label":' + \ '"' + self.labels_matrix[indexTab][indexRow].text() + '",\n' relayA = "" relayB = "" for z in range(self.nrele): if self.radio1cb_matrix[indexTab][indexRow][z].isChecked() == True: relayA += "1" else: relayA += "0" if self.radio2cb_matrix[indexTab][indexRow][z].isChecked() == True: relayB += "1" else: relayB += "0" jsonconfig += '\t\t\t"relayA":' + '"' + relayA + '",\n\t\t\t"relayB":' + \ '"' + relayB + '"\n\t\t}' if nRowChecked > 0: # chiudo parentesi prima di label jsonconfig += '\n\t}' jsonconfig += '\n}}' return jsonconfig def saveConfigurationFile(self, filename): """ prendo il primo elemento, cioe' 0 perche' getSaveFileName mi restituisce una lista del tipo (u'/home/giulio/workspace/amc/test-config.json', u'JSON Files (*.json)') """ fname = QFileDialog.getSaveFileName(self, 'Save configuration', os.getcwd(), "JSON Files (*.json)")[0] if fname: #print (fname) ###AtomicWrite.writeFile(fname, jsonconfig) self.saveConf(fname) #print ("Scrittura eseguita correttamente su file") def saveConf(self, filename): jsonconfig = self.createJsonConfiguration() AtomicWrite.writeFile(filename, jsonconfig) def loadConfigurationFile(self): """ apre una finestra di selezione in /home dove e' possibile selezionare solo file json prendo il primo elemento, cioe' 0 perche' getOpenFileName mi restituisce una lista del tipo (u'/home/giulio/workspace/amc/test-config.json', u'JSON Files (*.json)') """ fname = QFileDialog.getOpenFileName(self, 'Load configuration', os.getcwd(), "JSON Files (*.json)")[0] if fname: #print (fname) try: with open(fname, 'r') as fin: config = json.load(fin) #print (config) for indexTab in range(len(self.bands)): for indexRow in range(self.nrow): if self.checkbox_matrix[indexTab][indexRow].isChecked() == True: self.checkbox_matrix[indexTab][indexRow].setCheckState(Qt.Unchecked) for tab in config['relayconfig']: for row in config['relayconfig'][tab]: #print (tab + "-" + row) label = config['relayconfig'][tab][row]['label'] relayA = config['relayconfig'][tab][row]['relayA'] relayB = config['relayconfig'][tab][row]['relayB'] #print (label + relayA + relayB) self.checkbox_matrix[self.bands.index(tab)][int(row)].setCheckState(Qt.Checked) self.labels_matrix[self.bands.index(tab)][int(row)].setText(label) for i in range(self.nrele): if relayA[i] == '1': self.radio1cb_matrix[self.bands.index(tab)][int(row)][i].setCheckState(Qt.Checked) if relayB[i] == '1': self.radio2cb_matrix[self.bands.index(tab)][int(row)][i].setCheckState(Qt.Checked) except Exception as e: QMessageBox.warning(self, 'Errore', str(e)) def loadConfiguration(self, config): try: for indexTab in range(len(self.bands)): for indexRow in range(self.nrow): if self.checkbox_matrix[indexTab][indexRow].isChecked() == True: self.checkbox_matrix[indexTab][indexRow].setCheckState(Qt.Unchecked) for tab in config['relayconfig']: for row in config['relayconfig'][tab]: #print (tab + "-" + row) label = config['relayconfig'][tab][row]['label'] relayA = config['relayconfig'][tab][row]['relayA'] relayB = config['relayconfig'][tab][row]['relayB'] #print (label + relayA + relayB) self.checkbox_matrix[self.bands.index(tab)][int(row)].setCheckState(Qt.Checked) self.labels_matrix[self.bands.index(tab)][int(row)].setText(label) for i in range(self.nrele): if relayA[i] == '1': self.radio1cb_matrix[self.bands.index(tab)][int(row)][i].setCheckState(Qt.Checked) if relayB[i] == '1': self.radio2cb_matrix[self.bands.index(tab)][int(row)][i].setCheckState(Qt.Checked) except Exception as e: QMessageBox.warning(self, 'Errore', str(e)) def uploadConfiguration(self): self.saveConf("current.json") uploadConfiguration_panel = UploadConfigurationPanel() #uploadConfiguration_panel.show() uploadConfiguration_panel.exec_() def downloadConfiguration(self): downloadConfiguration_panel = DownloadConfigurationPanel() #uploadConfiguration_panel.show() downloadConfiguration_panel.exec_() config = downloadConfiguration_panel.getConfig() if config: self.loadConfiguration(config)
class Main(QMainWindow): ''' Main class inherits from QMainWindow''' def __init__(self): ''' initialises the class with essential variables and objects :return: None ''' super().__init__() self.setWindowTitle('FOSSEE SpreadSheet') self.resize(800, 600) self.work_books = 1 self.Tab_widget = QTabWidget() self.vBox = QVBoxLayout() Central_widget = QWidget() Central_widget.setLayout(self.vBox) self.work_booksList = [] self.work_booksList.append(Sheet()) self.work_booksList[0].setColumnHeaders() self.vBox.addWidget(self.Tab_widget) self.setTab() self.setCentralWidget(Central_widget) self.show() self.setMenu() def setTab(self, index=0, name="Untitled"): ''' Add Tabs to QTabWidget for number :param index: current index of Tab in QTabWidget :param name: sets the title of tab according to csv file name :return: None ''' self.Tab_widget.addTab(self.work_booksList[index].form_widget, name) def setMenu(self): ''' Sets the menu, toolbar, user interface :return: None ''' #setup actions quit_action = QAction('Quit', self) load_action = QAction('Load inputs', self) validate_action = QAction('Validate', self) submit_action = QAction('Submit', self) #setup triggered action load_action.triggered.connect(self.open_sheet) validate_action.triggered.connect(self.validate_sheet) submit_action.triggered.connect(self.submit_sheet) quit_action.triggered.connect(self.quit_app) #setup Toolbar toolbar = QMainWindow.addToolBar(self, 'Toolbar') toolbar.addAction(load_action) toolbar.addAction(validate_action) toolbar.addAction(submit_action) toolbar.addAction(quit_action) def xlsxToCsv(self, path): '''Converts the xlsx file to csv file format for each workbook :param path: Directory path for the file :return: None ''' wb = xlrd.open_workbook(path, on_demand=True) self.work_books = wb.nsheets for i in range(self.work_books): sh = wb.sheet_by_index(i) your_csv_file = open('your_csv_file' + str(i) + '.csv', 'w') wr = csv.writer(your_csv_file, quoting=csv.QUOTE_ALL, lineterminator='\n') for rownum in range(sh.nrows): wr.writerow(sh.row_values(rownum)) your_csv_file.close() return [['your_csv_file' + str(i) + '.csv', wb.sheet_names()[i]] for i in range(self.work_books)] def open_sheet(self): ''' calls file browser to open the csv,xlsx file :return: workbooklist - contains class sheet object list paths - list of path for each csv file ''' paths = [''] self.work_booksList[0].form_widget.check_change = False path = QFileDialog.getOpenFileName(self, 'Open CSV/XLS', os.getenv('HOME'), 'CSV(*.csv *.xlsx)') if path[0] == '': return paths[0] = path[0] sheet_names = [path[0].split('/')[-1].split('.')[0]] if path[0].split('/')[-1].split('.')[-1] == 'xlsx': paths.clear() paths, sheet_names = list(zip(*self.xlsxToCsv(path[0]))) else: self.work_books = 1 #changes needed to open multiple WB self.work_booksList[0].form_widget.deleteLater() self.Tab_widget.removeTab(0) self.work_booksList.clear() for g in range(self.work_books): self.work_booksList.append(Sheet()) self.setTab(g, sheet_names[g]) self.work_booksList[g].form_widget.check_change = False def Sheets(Workbooks, paths): ''' repopulates the each table with each csv file :param Workbooks: list of class sheet objects :param paths: list of paths to the each csv files :return: None ''' for Workbook, path in zip(Workbooks, paths): with open(path, newline='', encoding='utf-8', errors='ignore') as csv_file: Workbook.form_widget.setRowCount(0) #Workbook.form_widget.setColumnCount(10) my_file = csv.reader(csv_file, dialect='excel') fields = next(my_file) Workbook.col_headers = list( filter(lambda x: x != "", fields)) if len(fields) < 26: Workbook.form_widget.ncols = len(fields) Workbook.form_widget.setColumnCount(26) Workbook.setColumnHeaders() for row_data in my_file: row = Workbook.form_widget.rowCount() Workbook.form_widget.insertRow(row) for column, stuff in enumerate(row_data): item = QTableWidgetItem(stuff) Workbook.form_widget.setItem(row, column, item) if Workbook.form_widget.rowCount() < 26: Workbook.form_widget.nrows = Workbook.form_widget.rowCount( ) Workbook.form_widget.setRowCount(26) Workbook.form_widget.check_change = True for w in range(self.work_books): try: remove('your_csv_file{0:d}.csv'.format(w)) except: pass return Sheets(self.work_booksList, paths) def validate_sheet(self): ''' validates the workbook table if contains bad values calls message box :return: None ''' Bad_val = [] ID_col = [] flag = 'NoDuplicateID' cur_workbook = self.Tab_widget.currentIndex() ncols = self.work_booksList[cur_workbook].form_widget.ncols nrows = self.work_booksList[cur_workbook].form_widget.nrows for i in range(0, ncols): for j in range(0, nrows): if self.work_booksList[cur_workbook].form_widget.item( j, i) is not None: if (self.work_booksList[cur_workbook].form_widget.item( j, i).text()).replace('.', '').isnumeric() == False: Bad_val.append([i, j]) elif i == 0: ID_col.append( self.work_booksList[cur_workbook].form_widget.item( j, i).text()) #checking duplicate IDs if len(ID_col) > len(set(ID_col)): flag = 'DuplicateID' if len(Bad_val) > 0 or flag == 'DuplicateID': self.work_booksList[cur_workbook].form_widget.messageBox( Bad_val, flag) def submit_sheet(self): ''' creates the text file of each row of workbook containg the data of that row :return: None ''' cur_workbookIndex = self.Tab_widget.currentIndex() cur_workbookTitle = self.Tab_widget.tabText(cur_workbookIndex) ncols = self.work_booksList[cur_workbookIndex].form_widget.ncols nrows = self.work_booksList[cur_workbookIndex].form_widget.nrows Dictonary = {} for i in range(0, nrows): for j in range(0, ncols): if self.work_booksList[cur_workbookIndex].form_widget.item( i, j) is not None: Dictonary[ self.work_booksList[cur_workbookIndex].form_widget. horizontalHeaderItem(j).text()] = self.work_booksList[ cur_workbookIndex].form_widget.item(i, j).text() if self.work_booksList[cur_workbookIndex].form_widget.item( i, 0) is not None: with open( '{0:s}_{1:s}.txt'.format( cur_workbookTitle, self.work_booksList[cur_workbookIndex].form_widget. item(i, 0).text()), 'w') as file: file.write( dumps(Dictonary).replace('{', '').replace('}', '')) Dictonary.clear() def quit_app(self): ''' Quits the window and closes the app :return: None ''' qApp.quit()
class TfrmBase(QMainWindow, TScreenStates): recordCount = 0 def __init__(self, parent=None): super(TfrmBase, self).__init__() self.FOnStateChange = self.onStateChange self.activeState = self.ssInactive self._defaultSettings() self._createWidgets() self._setEvents() def _defaultSettings(self): self.setObjectName("frmBase") self.resize(640, 480) self.setMinimumSize(QSize(640, 480)) def _createWidgets(self): self._createLayout() self._createMenus() self._createToolBar() self._createStatusBar() self._createPages() self._setLayouts() def _createLayout(self): self.clientArea = QWidget() self.clientArea.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.clientArea.setMinimumSize(QSize(640, 400)) self.clientArea.setBaseSize(QSize(640, 400)) self.clientArea.setLayoutDirection(Qt.LeftToRight) self.clientArea.setObjectName("clientArea") self.gridLayout = QGridLayout(self.clientArea) self.gridLayout.setContentsMargins(0, 0, 0, 0) self.gridLayout.setSpacing(0) self.gridLayout.setObjectName("gridLayout") def _createMenus(self): # Create a Menu Bar self.mnMenu = self.menuBar() self.mnMenu.setObjectName("mnMenu") # Create all Top Menus self.mnApp = QMenu('&Aplicação') self.mnApp.setObjectName('mnApp') self.mnOpe = QMenu('&Operação') self.mnOpe.setObjectName("mnOperations") self.mnNav = QMenu('&Navegação') self.mnNav.setObjectName("mnNav") # Set Menus to MenuBar self.mnMenu.addMenu(self.mnNav) self.mnMenu.addMenu(self.mnOpe) self.mnMenu.addMenu(self.mnApp) # Crealte all Actions to Application Menu self._createAppActions() self._createOpeActions() self._setMenuActions() self.mnMenu.addAction(self.mnApp.menuAction()) self.mnMenu.addAction(self.mnOpe.menuAction()) self.mnMenu.addAction(self.mnNav.menuAction()) self._settingActionsEvents() def _createAppActions(self): # Exit Program Action self.acExit = QAction( self.getIcon("./resources/exit.ico", QSize(32, 32)), '&Sair') self.acExit.setObjectName("acExit") self.acExit.setShortcut('Ctrl+Q') self.acExit.setStatusTip('Finalizar o Programa') self.acExit.triggered.connect(self.closeApp) def _createOpeActions(self): # Search Action self.acSearch = QAction( self.getIcon("./resources/Search.ico", QSize(32, 32)), '&Pesquisar') self.acSearch.setObjectName("acSearch") self.acSearch.setShortcut('F5,Ctrl+P') self.acSearch.setStatusTip( 'Preenche o Filtro para Selecionar Registros') # List Action self.acList = QAction( self.getIcon("./resources/list.ico", QSize(32, 32)), '&Listar') self.acList.setShortcut('Ctrl+L') self.acList.setStatusTip('Listar todos os Registros') self.acList.setObjectName("acList") # Insert Action self.acInsert = QAction( self.getIcon("./resources/db_add.ico", QSize(32, 32)), '&Inserir') self.acInsert.setShortcut('F2,Ins') self.acInsert.setStatusTip('Incluir Novo Registros') self.acInsert.setObjectName("acInsert") # Update Action self.acUpdate = QAction( self.getIcon("./resources/db_update.ico", QSize(32, 32)), '&Editar') self.acUpdate.setShortcut('Ctrl+U') self.acUpdate.setStatusTip('Editar o Registro Atual') self.acUpdate.setObjectName("acUpdate") # Delete Action self.acDelete = QAction( self.getIcon("./resources/db_remove.ico", QSize(32, 32)), '&Excluir') self.acDelete.setShortcut('Ctrl+Del') self.acDelete.setStatusTip('Exclui o Registro Atual') self.acDelete.setObjectName("acDelete") # Save Action self.acSave = QAction( self.getIcon("./resources/db_commit.ico", QSize(32, 32)), '&Salvar') self.acSave.setShortcut('F10,Ctrl+S') self.acSave.setStatusTip('Salva as Alterações do Registro') self.acSave.setObjectName("acSave") # Cancel Action self.acCancel = QAction( self.getIcon("./resources/cancel.ico", QSize(32, 32)), '&Cancelar') self.acCancel.setShortcut('Esc') self.acCancel.setStatusTip('Cancela as Alterações do Registro') self.acCancel.setObjectName("acCancel") # First Action self.acFirst = QAction( self.getIcon("./resources/start.ico", QSize(32, 32)), '&Início') self.acFirst.setShortcut('Ctrl+Left') self.acFirst.setStatusTip('Vai para o Primeiro Registro') self.acFirst.setObjectName("acFirst") # Prior Action self.acPrior = QAction( self.getIcon("./resources/left.ico", QSize(32, 32)), '&Anterior') self.acPrior.setShortcut('Left') self.acPrior.setStatusTip('Vai para o Registro Anterior') self.acPrior.setObjectName("acPrior") # Next Action self.acNext = QAction( self.getIcon("./resources/right.ico", QSize(32, 32)), '&Próximo') self.acNext.setShortcut('Right') self.acNext.setStatusTip('Vai para o Próximo Registro') self.acNext.setObjectName("acNext") # Last Action self.acLast = QAction( self.getIcon("./resources/end.ico", QSize(32, 32)), '&Último') self.acLast.setShortcut('Ctrl+Right') self.acLast.setStatusTip('Vai para o Último Registro') self.acLast.setObjectName("acLast") # Form Title Action self.dcTitle = QAction() font = QFont() font.setPointSize(14) font.setBold(True) font.setWeight(75) self.dcTitle.setFont(font) self.dcTitle.setObjectName("dcTitle") def getIcon(self, res: str, size: QSize) -> QIcon: icon = QIcon() icon.addPixmap( QPixmap(res).scaled(size.width(), size.height(), Qt.KeepAspectRatio), QIcon.Active, QIcon.On) return icon def _setMenuActions(self): # Set Menu Application Actions self.mnApp.addAction(self.acExit) # Set Menu Operations Actions self.mnOpe.addAction(self.acSearch) self.mnOpe.addAction(self.acList) self.mnOpe.addSeparator() self.mnOpe.addAction(self.acInsert) self.mnOpe.addAction(self.acUpdate) self.mnOpe.addAction(self.acDelete) self.mnOpe.addSeparator() self.mnOpe.addAction(self.acSave) self.mnOpe.addAction(self.acCancel) # Set Menu Navigation Actions self.mnNav.addAction(self.acFirst) self.mnNav.addAction(self.acPrior) self.mnNav.addAction(self.acNext) self.mnNav.addAction(self.acLast) def _settingActionsEvents(self): # Set Menu Operations Trigger onClick self.acSearch.triggered.connect( lambda: self.setFormStatus(self.ssSearch)) self.acList.triggered.connect( lambda: self.setFormStatus(self.ssSearchAll)) self.acInsert.triggered.connect( lambda: self.setFormStatus(self.ssInsert)) self.acUpdate.triggered.connect( lambda: self.setFormStatus(self.ssUpdate)) self.acDelete.triggered.connect( lambda: self.setFormStatus(self.ssDelete)) self.acSave.triggered.connect(lambda: self.setFormStatus(self.ssPost)) self.acCancel.triggered.connect( lambda: self.setFormStatus(self.ssCancel)) # Set Menu Navigation Trigger onClick self.acFirst.triggered.connect( lambda: self.setFormStatus(self.ssFirst)) self.acPrior.triggered.connect( lambda: self.setFormStatus(self.ssPrior)) self.acNext.triggered.connect(lambda: self.setFormStatus(self.ssNext)) self.acLast.triggered.connect(lambda: self.setFormStatus(self.ssLast)) def _createToolBar(self): # Create a tbActions ToolBar self.tbActions = QToolBar() self.tbActions.setMinimumSize(QSize(300, 34)) self.tbActions.setMaximumSize(QSize(16777215, 34)) self.tbActions.setBaseSize(QSize(300, 34)) self.tbActions.setAcceptDrops(False) self.tbActions.setToolTipDuration(3) self.tbActions.setAllowedAreas(Qt.TopToolBarArea) self.tbActions.setObjectName("tbActions") self.addToolBar(Qt.TopToolBarArea, self.tbActions) # Create a tbTitle ToolBar self.tbTitle = QToolBar() self.tbTitle.setMinimumSize(QSize(340, 34)) self.tbTitle.setMaximumSize(QSize(16777215, 34)) self.tbTitle.setBaseSize(QSize(341, 34)) self.tbTitle.setAllowedAreas(Qt.TopToolBarArea) self.tbTitle.setToolButtonStyle(Qt.ToolButtonTextOnly) self.tbTitle.setFloatable(False) self.tbTitle.setObjectName("tbTitle") # self.tbTitle.setLabelAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.addToolBar(Qt.TopToolBarArea, self.tbTitle) # Call Add Actions to ToolBar self._setToolBarActions() def _setToolBarActions(self): # Set ToolBar Actions self.tbActions.addAction(self.acSearch) self.tbActions.addAction(self.acInsert) self.tbActions.addAction(self.acUpdate) self.tbActions.addAction(self.acDelete) self.tbActions.addSeparator() self.tbActions.addAction(self.acSave) self.tbActions.addAction(self.acExit) self.tbTitle.addAction(self.dcTitle) def _createStatusBar(self): self.sbStatus = QStatusBar() self.sbStatus.setMaximumHeight(24) self.sbStatus.setObjectName("sbStatus") self.sbStatus.setStyleSheet(""" .QLabel { background-color: #FFFFFF; color: #000000; } """) self.lbStatus = QLabel(self.sbStatus) self.lbStatus.setAlignment(Qt.AlignHCenter | Qt.AlignVCenter) self.lbStatus.setText('Inactive') self.lbStatus.setMinimumSize(QSize(130, 15)) self.lbStatus.setFrameShape(QFrame.Panel) self.lbStatus.setFrameShadow(QFrame.Sunken) self.sbStatus.addPermanentWidget(self.lbStatus) self.setStatusBar(self.sbStatus) def _createPages(self): self.tabMain = QTabWidget(self.clientArea) self.tabMain.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.tabMain.setTabPosition(QTabWidget.South) self.tabMain.setObjectName("tabMain") self.pgList = QWidget(self.tabMain) self.pgList.setObjectName("pgList") self.pgDetail = QWidget(self.tabMain) self.pgDetail.setObjectName("pgDetail") self.tabMain.addTab(self.pgList, "") self.tabMain.addTab(self.pgDetail, "") self._createPageListContent() def _createPageListContent(self): self.treeWidget = QTreeWidget(self.pgList) self.treeWidget.setSizePolicy( QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)) self.treeWidget.setFrameShape(QFrame.NoFrame) self.treeWidget.setFrameShadow(QFrame.Plain) self.treeWidget.setColumnCount(3) self.treeWidget.setObjectName("treeWidget") self.treeWidget.headerItem().setText(0, "Campo") self.treeWidget.headerItem().setText(1, "Campo") self.treeWidget.headerItem().setText(2, "Campo") self.treeWidget.setGeometry(QRect(0, 0, 640, 370)) self.treeWidget.setMinimumSize(QSize(640, 370)) self.tabMain.setCurrentIndex(0) def _setLayouts(self): self.gridLayout.addWidget(self.tabMain, 0, Qt.AlignBottom | Qt.AlignRight, 1, 1) self.setCentralWidget(self.clientArea) def translateForm(self): self._translate = QCoreApplication.translate self.setWindowTitle( self._translate("TfrmBase", "Tela de Básica de Cadastros")) self.mnApp.setTitle(self._translate("TfrmBase", "Aplicação")) self.mnOpe.setTitle(self._translate("TfrmBase", "Operações")) self.mnNav.setTitle(self._translate("TfrmBase", "Navegação")) self.sbStatus.setToolTip(self._translate("TfrmBase", "Barra de Status")) self.tbActions.setWindowTitle( self._translate("TfrmBase", "Ferramentas")) self.tbActions.setToolTip( self._translate("TfrmBase", "Barra de Ferramentas")) self.tbTitle.setWindowTitle(self._translate("TfrmBase", "Descrição")) self.acExit.setText(self._translate("TfrmBase", "&Sair")) self.acExit.setToolTip(self._translate("TfrmBase", "Sair do Programa")) self.acSearch.setText(self._translate("TfrmBase", "&Pesquisar")) self.acSearch.setStatusTip( self._translate("TfrmBase", "Procurar Por um Registro")) self.acList.setText(self._translate("TfrmBase", "&Listar Todos")) self.acList.setStatusTip( self._translate("TfrmBase", "Lista todos os Registros")) self.acInsert.setText(self._translate("TfrmBase", "&Inserir")) self.acInsert.setStatusTip( self._translate("TfrmBase", "Adicionar Registro")) self.acUpdate.setText(self._translate("TfrmBase", "&Editar")) self.acUpdate.setStatusTip( self._translate("TfrmBase", "Editar Registro")) self.acDelete.setText(self._translate("TfrmBase", "E&xcluir")) self.acDelete.setStatusTip( self._translate("TfrmBase", "Excluir Registro")) self.acSave.setText(self._translate("TfrmBase", "&Salvar")) self.acSave.setToolTip(self._translate("TfrmBase", "Salvar Registro")) self.acCancel.setText(self._translate("TfrmBase", "&Cancelar")) self.acCancel.setToolTip( self._translate("TfrmBase", "Cencelar Alterações")) self.dcTitle.setText( self._translate("TfrmBase", "Título da Tela de Cadastros")) self.dcTitle.setToolTip( self._translate("TfrmBase", "Título da Tela de Cadastros")) self.tabMain.setTabText( self.tabMain.indexOf(self.pgList), self._translate("TfrmBase", "Lista dos Registros")) self.tabMain.setTabToolTip( self.tabMain.indexOf(self.pgList), self._translate("TfrmBase", "Listagem das Ferramentas")) self.tabMain.setTabText( self.tabMain.indexOf(self.pgDetail), self._translate("TfrmBase", "Detalhes do Registro Selecionando")) @property def activeState(self): return self._activeValue @property def activeStateColor(self): return self.activeValue['FG'] @property def activeStateBackgroud(self): return self.activeValue['BG'] @activeState.setter # Seta a Propriedade _activeState def activeState(self, value: int): self.workValue = value self._activeState = value def setScreenState(self, stt: int): self.acExit.setEnabled(self.inBrowse(stt)) # Set Menu Operations Actions self.acSearch.setEnabled((self.inBrowse(stt) or (self.recordCount == 0))) self.acList.setEnabled((self.inBrowse(stt) or (self.recordCount == 0))) self.acInsert.setEnabled(self.inBrowse(stt)) self.acUpdate.setEnabled((self.inBrowse(stt) and (self.recordCount > 0))) self.acDelete.setEnabled((self.inBrowse(stt) and (self.recordCount > 0))) self.acSave.setEnabled(self.inUpdate(stt)) self.acCancel.setEnabled(self.inUpdate(stt)) # Set Menu Navigation Actions self.acFirst.setEnabled((self.inBrowse(stt) and (self.recordCount > 0))) self.acPrior.setEnabled((self.inBrowse(stt) and (self.recordCount > 0))) self.acNext.setEnabled((self.inBrowse(stt) and (self.recordCount > 0))) self.acLast.setEnabled((self.inBrowse(stt) and (self.recordCount > 0))) # Set tab Main if state in Browse enabled self.tabMain.setEnabled(self.inBrowse(stt)) def _layoutWidgets(self): return (self.frmLayout.itemAt(i) for i in range(self.frmLayout.count())) def _getAllFields(self): arrFields = [] for w in self._layoutWidgets(): if (not (isinstance(w, QLabel))): arrFields.append(w) return arrFields def setEnableFields(self, enable: bool = True): # Enable All Fields for controls in self._layoutWidgets(): QWidget(controls).setEnabled(enable) def clearFields(self): # cliar content of all fileds for controls in self._getAllFields(): QWidget(controls).setText('') def setColorFields(self): # cliar content of all fileds style = ".QWidget { backgroud-color: " + self.activeStateBackgroud + "; }" for controls in self._getAllFields(): QWidget(controls).setStyle(style) def showDataDetails(self): # move data of selected record to fileds if (self.tabMain.currentIndex() == 0): self.tabMain.setCurrentIndex(1) def filterRecord(self): raise NotImplementedError(500) def getFirstRecord(self): raise NotImplementedError(500) def getPriorRecord(self): raise NotImplementedError(500) def getNextRecord(self): raise NotImplementedError(500) def getLastRecord(self): raise NotImplementedError(500) def insertRecord(self): raise NotImplementedError(500) def deleteRecord(self): raise NotImplementedError(500) def updateRecord(self): raise NotImplementedError(500) def postRecord(self): raise NotImplementedError(500) def execOpertations(self, state: int): if ((state == self.ssFilter) or (state == self.ssSearchAll)): self.filterRecord() elif (state == self.ssFirst): self.getFirstRecord() elif (state == self.ssPrior): self.getPriorRecord() elif (state == self.ssNext): self.getNextRecord() elif (state == self.ssLast): self.getLastRecord() elif (state == self.ssInsert): self.insertRecord() elif (state == self.ssDelete): self.deleteRecord() elif (state == self.ssUpdate): self.updateRecord() elif (state == self.ssPost): self.postRecord() else: raise NotImplementedError(401, 'Operação não suportada') @pyqtSlot(int) def setFormStatus(self, state: int): if ((state == self.ssSearch) and (self.activeState != state)): self.clearFields() self.setColorFields() self.showDataDetails() if (self.activeState != state): self.activeState = state if (state == self.ssCancel): self.activeState = self.ssBrowse @pyqtSlot(int, int, dict, str) def onStateChange(self, NewState: int, OldState: int, Result: dict = {}, Err: str = ''): try: # show screen state on status bar state = self.getStateProperties(NewState) style = '.QLabel { background-color: ' + state[ 'BG'] + '; color: ' + state['FG'] + '; }' self.sbStatus.setStyleSheet(style) self.lbStatus.setText(state['Descr']) # call operation into child screen self.execOpertations(NewState) # change buttons states self.setScreenState(NewState) # set result status code and result satatus Message self.setResultStatusCode = 200 self.setResultStatusMessage = '' except Exception as e: self.ResultStatusCode = 200 self.ResultStatusMessage = str(e) QMessageBox.critical(self, self.windowTitle(), self.ResultStatusMessage) return self.result @pyqtSlot() def tabMainChanged(self): self.sbStatus.showMessage('TabMain change tabIndex to (' + str(self.tabMain.currentIndex()) + ')!') if (self.tabMain.currentIndex() == 1): self.showDataDetails() @pyqtSlot() def InsertData(self): # self.sbStatus.showMessage('Prepare to insert data....') pass def _setEvents(self): self.tabMain.blockSignals( True) # just for not showing the initial message self.tabMain.currentChanged.connect(self.tabMainChanged) # changed! self.tabMain.blockSignals(False) # wait signals now @pyqtSlot() def closeApp(self): self.close()
class MainWindow(QMainWindow): def __init__(self, parent=None): super(MainWindow, self).__init__(parent) self.fnames = [] # list of file names to be converted self.office_listener_started = False self.parse_cla() addQPB = QPushButton(self.tr('Add')) delQPB = QPushButton(self.tr('Delete')) clearQPB = QPushButton(self.tr('Clear')) vlayout1 = utils.add_to_layout('v', addQPB, delQPB, clearQPB, None) self.filesList = utils.FilesList() self.filesList.setSelectionMode(QAbstractItemView.ExtendedSelection) hlayout1 = utils.add_to_layout('h', self.filesList, vlayout1) outputQL = QLabel(self.tr('Output folder:')) self.toQLE = QLineEdit() self.toQLE.setReadOnly(True) self.toQTB = QToolButton() self.toQTB.setText('...') hlayout2 = utils.add_to_layout('h', outputQL, self.toQLE, self.toQTB) self.audiovideo_tab = AudioVideoTab(self) self.image_tab = ImageTab(self) self.document_tab = DocumentTab(self) self.tabs = [self.audiovideo_tab, self.image_tab, self.document_tab] tab_names = [self.tr('Audio/Video'), self.tr('Images'), self.tr('Documents')] self.tabWidget = QTabWidget() for num, tab in enumerate(tab_names): self.tabWidget.addTab(self.tabs[num], tab) self.tabWidget.setCurrentIndex(0) self.origQCB = QCheckBox( self.tr('Save each file in the same\nfolder as input file')) self.deleteQCB = QCheckBox(self.tr('Delete original')) convertQPB = QPushButton(self.tr('&Convert')) hlayout3 = utils.add_to_layout('h', self.origQCB, self.deleteQCB, None) hlayout4 = utils.add_to_layout('h', None, convertQPB) final_layout = utils.add_to_layout( 'v', hlayout1, self.tabWidget, hlayout2, hlayout3, hlayout4) self.dependenciesQL = QLabel() self.statusBar().addPermanentWidget(self.dependenciesQL, stretch=1) widget = QWidget() widget.setLayout(final_layout) self.setCentralWidget(widget) openAction = utils.create_action( self, self.tr('Open'), QKeySequence.Open, None, self.tr('Open a file'), self.filesList_add ) convertAction = utils.create_action( self, self.tr('Convert'), 'Ctrl+C', None, self.tr('Convert files'), self.start_conversion ) quitAction = utils.create_action( self, self.tr('Quit'), 'Ctrl+Q', None, self.tr('Quit'), self.close ) edit_presetsAction = utils.create_action( self, self.tr('Edit Presets'), 'Ctrl+P', None, self.tr('Edit Presets'), self.open_dialog_presets ) importAction = utils.create_action( self, self.tr('Import'), None, None, self.tr('Import presets'), self.import_presets ) exportAction = utils.create_action( self, self.tr('Export'), None, None, self.tr('Export presets'), self.export_presets ) resetAction = utils.create_action( self, self.tr('Reset'), None, None, self.tr('Reset presets'), self.reset_presets ) syncAction = utils.create_action( self, self.tr('Synchronize'), None, None, self.tr('Synchronize presets'), self.sync_presets ) removeoldAction = utils.create_action( self, self.tr('Remove old'), None, None, self.tr('Remove old presets'), self.removeold_presets ) clearallAction = utils.create_action( self, self.tr('Clear All'), None, None, self.tr('Clear form'), self.clear_all ) preferencesAction = utils.create_action( self, self.tr('Preferences'), 'Alt+Ctrl+P', None, self.tr('Preferences'), self.open_dialog_preferences ) trackerAction = utils.create_action( self, 'Issue tracker', None, None, None, lambda: webbrowser.open( "https://github.com/Ilias95/FF-Multi-Converter/issues") ) wikiAction = utils.create_action( self, 'Wiki', None, None, None, lambda: webbrowser.open( "https://github.com/Ilias95/FF-Multi-Converter/wiki") ) ffmpegdocAction = utils.create_action( self, 'FFmpeg ' + self.tr('documentation'), None, None, None, lambda: webbrowser.open( "https://www.ffmpeg.org/documentation.html") ) imagemagickdocAction = utils.create_action( self, 'ImageMagick ' + self.tr('documentation'), None, None, None, lambda: webbrowser.open( "http://www.imagemagick.org/script/convert.php") ) aboutAction = utils.create_action( self, self.tr('About'), 'Ctrl+?', None, self.tr('About'), self.open_dialog_about ) fileMenu = self.menuBar().addMenu(self.tr('File')) editMenu = self.menuBar().addMenu(self.tr('Edit')) presetsMenu = self.menuBar().addMenu(self.tr('Presets')) helpMenu = self.menuBar().addMenu(self.tr('Help')) utils.add_actions( fileMenu, [openAction, convertAction, None, quitAction]) utils.add_actions( presetsMenu, [edit_presetsAction, importAction, exportAction, resetAction, None, syncAction, removeoldAction] ) utils.add_actions(editMenu, [clearallAction, None, preferencesAction]) utils.add_actions( helpMenu, [trackerAction, wikiAction, None, ffmpegdocAction, imagemagickdocAction, None, aboutAction] ) self.filesList.dropped.connect(self.filesList_add_dragged) addQPB.clicked.connect(self.filesList_add) delQPB.clicked.connect(self.filesList_delete) clearQPB.clicked.connect(self.filesList_clear) self.tabWidget.currentChanged.connect( lambda: self.tabs[0].moreQPB.setChecked(False)) self.origQCB.toggled.connect( lambda: self.toQLE.setEnabled(not self.origQCB.isChecked())) self.toQTB.clicked.connect(self.get_output_folder) convertQPB.clicked.connect(convertAction.triggered) del_shortcut = QShortcut(self) del_shortcut.setKey(Qt.Key_Delete) del_shortcut.activated.connect(self.filesList_delete) self.setWindowTitle('FF Multi Converter') self.load_settings() self.check_for_dependencies() self.audiovideo_tab.set_default_command() self.image_tab.set_default_command() self.toQLE.setText(self.default_output) self.filesList_update() def parse_cla(self): """Parse command line arguments.""" for i in QCoreApplication.arguments()[1:]: i = os.path.abspath(i) if os.path.isfile(i): self.fnames.append(i) else: print("ffmulticonverter: {0}: Not a file".format(i)) def check_for_dependencies(self): """ Check if each one of the program dependencies are installed and update self.dependenciesQL with the appropriate message. """ if not utils.is_installed(self.ffmpeg_path): self.ffmpeg_path = utils.is_installed('ffmpeg') QSettings().setValue('ffmpeg_path', self.ffmpeg_path) self.unoconv = utils.is_installed('unoconv') self.imagemagick = utils.is_installed('convert') missing = [] if not self.ffmpeg_path: missing.append('ffmpeg') if not self.unoconv: missing.append('unoconv') if not self.imagemagick: missing.append('imagemagick') if missing: missing = ', '.join(missing) status = self.tr('Missing dependencies:') + ' ' + missing self.dependenciesQL.setText(status) def load_settings(self): settings = QSettings() self.overwrite_existing = settings.value('overwrite_existing', type=bool) self.default_output = settings.value('default_output', type=str) self.prefix = settings.value('prefix', type=str) self.suffix = settings.value('suffix', type=str) self.ffmpeg_path = settings.value('ffmpeg_path', type=str) self.default_command = (settings.value('default_command', type=str) or config.default_ffmpeg_cmd) # type=list won't work for some reason extraformats_video = (settings.value('extraformats_video') or []) videocodecs = (settings.value('videocodecs') or config.video_codecs) audiocodecs = (settings.value('audiocodecs') or config.audio_codecs) self.default_command_image = (settings.value('default_command_image', type=str) or config.default_imagemagick_cmd) extraformats_image = (settings.value('extraformats_image') or []) extraformats_document = (settings.value('extraformats_document') or []) self.audiovideo_tab.fill_video_comboboxes(videocodecs, audiocodecs, extraformats_video) self.image_tab.fill_extension_combobox(extraformats_image) self.document_tab.fill_extension_combobox(extraformats_document) def get_current_tab(self): for i in self.tabs: if self.tabs.index(i) == self.tabWidget.currentIndex(): return i def filesList_update(self): self.filesList.clear() for i in self.fnames: self.filesList.addItem(i) def filesList_add(self): filters = 'All Files (*);;' filters += 'Audio/Video Files (*.{});;'.format( ' *.'.join(self.audiovideo_tab.formats)) filters += 'Image Files (*.{});;'.format( ' *.'.join(self.image_tab.formats + self.image_tab.extra_img)) filters += 'Document Files (*.{})'.format( ' *.'.join(self.document_tab.formats)) fnames = QFileDialog.getOpenFileNames(self, 'FF Multi Converter - ' + self.tr('Choose File'), config.home, filters, options=QFileDialog.HideNameFilterDetails)[0] if fnames: for i in fnames: if not i in self.fnames: self.fnames.append(i) self.filesList_update() def filesList_add_dragged(self, links): for path in links: if os.path.isfile(path) and not path in self.fnames: self.fnames.append(path) self.filesList_update() def filesList_delete(self): items = self.filesList.selectedItems() if items: for i in items: self.fnames.remove(i.text()) self.filesList_update() def filesList_clear(self): self.fnames = [] self.filesList_update() def clear_all(self): """Clears or sets to default the values of all graphical widgets.""" self.toQLE.clear() self.origQCB.setChecked(False) self.deleteQCB.setChecked(False) self.filesList_clear() self.audiovideo_tab.clear() self.image_tab.clear() def get_output_folder(self): if self.toQLE.isEnabled(): output = QFileDialog.getExistingDirectory( self, 'FF Multi Converter - ' + self.tr('Choose output destination'), config.home) if output: self.toQLE.setText(output) def import_presets(self): presets_dlgs.ShowPresets().import_presets() def export_presets(self): presets_dlgs.ShowPresets().export_presets() def reset_presets(self): presets_dlgs.ShowPresets().reset() def sync_presets(self): presets_dlgs.ShowPresets().synchronize() def removeold_presets(self): presets_dlgs.ShowPresets().remove_old() def ok_to_continue(self): """ Check if everything is ok to continue with conversion. Check if: - At least one file has given for conversion. - An output folder has given. - Output folder exists. Return False if an error arises, else True. """ try: if not self.fnames: raise ValidationError( self.tr('You must add at least one file to convert!')) elif not self.origQCB.isChecked() and not self.toQLE.text(): raise ValidationError( self.tr('You must choose an output folder!')) elif (not self.origQCB.isChecked() and not os.path.exists(self.toQLE.text())): raise ValidationError(self.tr('Output folder does not exists!')) if not self.get_current_tab().ok_to_continue(): return False return True except ValidationError as e: QMessageBox.warning( self, 'FF Multi Converter - ' + self.tr('Error!'), str(e)) return False def start_conversion(self): """ Extract the appropriate information from GUI and call the Progress dialog with the suitable argumens. """ if not self.ok_to_continue(): return tab = self.get_current_tab() ext_to = '.' + tab.extQCB.currentText() if tab.name == 'Documents' and not self.office_listener_started: utils.start_office_listener() self.office_listener_started = True _list = utils.create_paths_list( self.fnames, ext_to, self.prefix, self.suffix, self.toQLE.text(), self.origQCB.isChecked(), self.overwrite_existing ) dialog = progress.Progress( _list, tab, self.deleteQCB.isChecked(), self) dialog.show() def open_dialog_preferences(self): """Open the preferences dialog.""" dialog = preferences_dlg.Preferences(self) if dialog.exec_(): self.load_settings() def open_dialog_presets(self): """Open the presets dialog.""" dialog = presets_dlgs.ShowPresets(self) dialog.exec_() def open_dialog_about(self): """Call the about dialog with the appropriate values.""" msg = self.tr('Convert among several file types to other formats') msg = textwrap.fill(msg, 54).replace('\n', '<br>') text = '''<b> FF Multi Converter {0} </b> <p>{1} <p><a href="{2}">FF Multi Converter - Home Page</a> <p>Copyright © 2011-2016 {3} <br>License: {4} <p>Python {5} - Qt {6} - PyQt {7} on {8}'''\ .format(ffmc.__version__, msg, ffmc.__url__, ffmc.__author__, ffmc.__license__, platform.python_version()[:5], QT_VERSION_STR, PYQT_VERSION_STR, platform.system()) image = ':/ffmulticonverter.png' authors = '{0} <{1}>\n\n'.format(ffmc.__author__, ffmc.__author_email__) authors += 'Contributors:\nPanagiotis Mavrogiorgos' translators = [] for i in config.translators: translators.append('{0}\n {1}'.format(i[0], i[1])) translators = '\n\n'.join(translators) dialog = about_dlg.AboutDialog(text, image, authors, translators, self) dialog.exec_()
class Main_UI(QMainWindow): # Main Variables in this class. def __init__(self, auth_info): super(Main_UI, self).__init__() self.auth_info = auth_info self.exp_Class = auth_info['Class'] self.exp_username = auth_info['username'] self.exp_password = auth_info['password'] self.exp_dbname = auth_info['DBName'] self.db_conn = SQL.connect('localhost', auth_info['username'], auth_info['password'], auth_info['DBName']) self.full_width = QApplication.primaryScreen().size().width() - 15 self.full_height = QApplication.primaryScreen().size().height() - 68 self.Main_Grid = QGridLayout() self.toolbar = QToolBar() self.tabwidget = QTabWidget() self.win_icon = "Resources/sm_logo_icon.ico" self.win_title = "Sales Manager V1.0 | %s - %s" % (self.exp_Class, self.exp_username) self.table_label = { 'clients': ['ID', 'Name', 'Email', 'Username', 'Password', 'Number Phone'], 'products': ['ID', 'Product Name', 'Quantity', 'Unit Price', 'Product Code'], 'requests': [ 'ID', 'Client', 'Product Name', 'Quantity', 'Request Date', 'Status' ] } if self.exp_Class == "Importer": self.Importer_UI() elif self.exp_Class == "Exporter": self.Exporter_UI() #Eporter Interface def Exporter_UI(self): self.setGeometry(0, 0, self.full_width, self.full_height) self.setWindowTitle(self.win_title) self.setWindowIcon(QIcon(self.win_icon)) self.toolbar.setMovable(False) self.toolbar.setIconSize(QSize(32, 32)) # Tables in Exporter Interface # Table_1 - clients self.table_1 = QTableWidget() self.Hheader_1 = self.table_1.horizontalHeader() self.Vheader_1 = self.table_1.verticalHeader() self.table_1.setAlternatingRowColors(True) self.table_1.setColumnCount(6) self.Hheader_1.setCascadingSectionResizes(False) self.Hheader_1.setSortIndicatorShown(False) self.Hheader_1.setStretchLastSection(True) self.Vheader_1.setVisible(False) self.Vheader_1.setCascadingSectionResizes(False) self.Vheader_1.setStretchLastSection(False) self.table_1.setHorizontalHeaderLabels(self.table_label['clients']) # Table_2 - products self.table_2 = QTableWidget() self.Hheader_2 = self.table_2.horizontalHeader() self.Vheader_2 = self.table_2.verticalHeader() self.table_2.setAlternatingRowColors(True) self.table_2.setColumnCount(5) self.Hheader_2.setCascadingSectionResizes(False) self.Hheader_2.setSortIndicatorShown(False) self.Hheader_2.setStretchLastSection(True) self.Vheader_2.setVisible(False) self.Vheader_2.setCascadingSectionResizes(False) self.Vheader_2.setStretchLastSection(False) self.table_2.setHorizontalHeaderLabels(self.table_label['products']) # Table_3 - requests self.table_3 = QTableWidget() self.Hheader_3 = self.table_3.horizontalHeader() self.Vheader_3 = self.table_3.verticalHeader() self.table_3.setAlternatingRowColors(True) self.table_3.setColumnCount(6) self.Hheader_3.setCascadingSectionResizes(False) self.Hheader_3.setSortIndicatorShown(False) self.Hheader_3.setStretchLastSection(True) self.Vheader_3.setVisible(False) self.Vheader_3.setCascadingSectionResizes(False) self.Vheader_3.setStretchLastSection(False) self.table_3.setHorizontalHeaderLabels(self.table_label['requests']) # Actions in toolbar self.ac_savetable = QAction(QIcon('Resources/save_as.png'), 'Save Table', self) self.ac_addclient = QAction(QIcon('Resources/add-user-male-64.png'), 'Add Client', self) self.ac_updateclient = QAction(QIcon('Resources/change-user-64.png'), 'Update Client', self) self.ac_deleteclient = QAction(QIcon('Resources/denied-64.png'), 'Delete Client', self) self.ac_addproduct = QAction(QIcon('Resources/add_tag.png'), 'Add Product', self) self.ac_updateproduct = QAction(QIcon('Resources/update_tag.png'), 'Update Product', self) self.ac_deleteproduct = QAction(QIcon('Resources/remove_tag.png'), 'Delete Product', self) self.ac_refreshtable = QAction(QIcon('Resources/refresh.png'), 'Refresh Table', self) self.ac_aboutapp = QAction(QIcon('Resources/info.png'), 'About App', self) self.ac_developerinfo = QAction(QIcon('Resources/developer_info.png'), 'Developer Info', self) self.ac_contactus = QAction(QIcon('Resources/contact_us.png'), 'Contact Us', self) # Add Actions to toolbar. actions = [ self.ac_savetable, self.ac_addclient, self.ac_updateclient, self.ac_deleteclient, self.ac_addproduct, self.ac_deleteproduct, self.ac_updateproduct, self.ac_refreshtable, self.ac_aboutapp, self.ac_developerinfo ] for action in actions: self.toolbar.addAction(action) # Set Widgets self.tabwidget.addTab(self.table_1, 'clients') self.tabwidget.addTab(self.table_2, 'products') self.tabwidget.addTab(self.table_3, 'requests') self.addToolBar(self.toolbar) self.setCentralWidget(self.tabwidget) # Actions Connect self.ac_addclient.triggered.connect(self.addClient) self.ac_updateclient.triggered.connect(self.updateClient) self.ac_deleteclient.triggered.connect(self.deleteClient) self.ac_refreshtable.triggered.connect(lambda x: self.refresh( self.tabwidget.tabText(self.tabwidget.currentIndex()))) self.ac_aboutapp.triggered.connect(self.aboutApp) self.ac_developerinfo.triggered.connect(self.developerInfo) self.loadData(self.exp_Class) self.show() def Importer_UI(self): pass #LOad Data from Tables Method. def loadData(self, usrClass): if usrClass == "Exporter": db_cur = self.db_conn.cursor() db_cur.execute(" SELECT * FROM clients ") clients_data = db_cur.fetchall() db_cur.execute(" SELECT * FROM products ") products_data = db_cur.fetchall() db_cur.execute(" SELECT * FROM requests ") requests_data = db_cur.fetchall() # Get Clients Table self.table_1.setRowCount(0) for t1_index, t1_row in enumerate(clients_data): self.table_1.insertRow(t1_index) for t1_feature, t1_value in enumerate(t1_row): self.table_1.setItem(t1_index, t1_feature, QTableWidgetItem(str(t1_value))) # Get Products Table self.table_2.setRowCount(0) for t2_index, t2_row in enumerate(products_data): self.table_2.insertRow(t2_index) for t2_feature, t2_value in enumerate(t2_row): self.table_2.setItme(t2_index, t2_feature, QTableWidgetItem(str(t2_value))) # Get Requests Table self.table_3.setRowCount(0) for t3_index, t3_row in enumerate(requests_data): self.table_3.insertRow(t3_index) for t3_feature, t3_value in enumerate(t3_row): self.table_3.setItem(t3_index, t3_feature, QTableWidgetItem(str(t3_value))) db_cur.close() elif usrClass == "Importer": pass # Refresh Table method. def refresh(self, table): db_cur = self.db_conn.cursor() sql_query = """ SELECT * FROM {} """.format(table) db_cur.execute(sql_query) table_data = db_cur.fetchall() widget = self.table_1 if table == 'clients' else self.table_2 if table == 'products' else self.table_3 widget.setRowCount(0) for t1_index, t1_row in enumerate(table_data): widget.insertRow(t1_index) for t1_feature, t1_value in enumerate(t1_row): widget.setItem(t1_index, t1_feature, QTableWidgetItem(str(t1_value))) db_cur.close() # Add Client method - connect with Add Client Action def addClient(self): add_dialog = Add_Client(self.db_conn) add_dialog.exec_() # Update Client method - connect with Add Update Action def updateClient(self): update_dialog = Update_Client(self.db_conn) update_dialog.exec_() # Delete Client Method - connect with Delete Client Action def deleteClient(self): delete_dialog = Delete_Client(self.db_conn) delete_dialog.exec_() # Add Product Method - connect with Add Product Action def addProduct(self): pass # Update Product Method - connect with Update Product Action def aupdateProduct(self): pass # Delete Product Method - connect with Delete Product Action def deleteProduct(self): pass # About App method - connect with About App Action def aboutApp(self): about_app = About_App() about_app.exec_() # Developer Info method - connect with Developer Info Action def developerInfo(self): developer_info = Developer_Info() developer_info.exec_()
class EditorMainWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.ui = Ui_ScriptEditor() self.ui.setupUi(self) #self.ui.actionExit.triggered.connect(self.exit) self.splitter = QSplitter(Qt.Vertical, self) self.setCentralWidget(self.splitter) self.edit_tab = QTabWidget(self.splitter) self.console_tab = QTabWidget(self.splitter) self.py_console = PythonConsole(self.console_tab) self.console_tab.addTab(self.py_console, "&Python console") self.js_console = QtQmlConsole(self.console_tab) self.console_tab.addTab(self.js_console, "&QtQml console") self.editors = [] self.on_actionNewPython_triggered() @pyqtSlot() def closeEvent(self, event): while(self.editors.__len__()): edit = self.edit_tab.currentWidget() if edit: if(edit.isModified()): saveBox = SaveDialog("You have unsaved script. Save it now?") prompt = saveBox.exec_() if(prompt == QMessageBox.Save): event.ignore() self.save(True) elif(prompt == QMessageBox.Cancel): event.ignore() return elif(prompt == QMessageBox.Discard): event.accept() i = self.edit_tab.indexOf(edit) self.edit_tab.removeTab(i) self.editors.remove(edit) event.accept() @pyqtSlot() def on_actionExit_triggered(self): while(self.editors.__len__()): edit = self.edit_tab.currentWidget() if edit: if(edit.isModified()): saveBox = SaveDialog("You have unsaved script. Save it now?") prompt = saveBox.exec_() if(prompt == QMessageBox.Save): self.save(True) elif(prompt == QMessageBox.Cancel): return elif(prompt == QMessageBox.Discard): pass i = self.edit_tab.indexOf(edit) self.edit_tab.removeTab(i) self.editors.remove(edit) self.close() @pyqtSlot() def on_actionNewPython_triggered(self): pyedit = PythonEditorWidget(self.edit_tab) pyedit.setPlainText(template_py) self.edit_tab.addTab(pyedit, "Python") self.edit_tab.setCurrentWidget(pyedit) self.editors.append(pyedit) self.py_console.attach() self.console_tab.setCurrentIndex(0) pyedit.setFocus() pyedit.view.setFocus() @pyqtSlot() def on_actionNewQtQml_triggered(self): jsedit = QtQmlEditorWidget(self.edit_tab) self.edit_tab.addTab(jsedit, "QtQml") self.edit_tab.setCurrentWidget(jsedit) self.editors.append(jsedit) self.js_console.attach() self.console_tab.setCurrentIndex(1) @pyqtSlot() def on_actionClose_triggered(self): edit = self.edit_tab.currentWidget() if edit: if(edit.isModified()): saveBox = SaveDialog("Do you want to save this Script?") prompt = saveBox.exec_() if(prompt == QMessageBox.Save): self.save(True) elif(prompt == QMessageBox.Cancel): return elif(prompt == QMessageBox.Discard): pass i = self.edit_tab.indexOf(edit) self.edit_tab.removeTab(i) self.editors.remove(edit) @pyqtSlot() def on_actionClear_triggered(self): #edit = self.edit_tab.currentWidget() #edit.setPlainText(template_py) self.py_console.clear() @pyqtSlot() def on_actionSave_As_triggered(self): self.save() @pyqtSlot() def on_actionSave_triggered(self): self.save(True) #Path of the script file in each tab will be stored in tabToolTip def save(self, Update = False): edit = self.edit_tab.currentWidget() contents = str(edit.toPlainText()) if((Update == False) or (self.edit_tab.tabText(self.edit_tab.currentIndex()) == "Python") ): #Save in its first invocation and Save As will enter filename = QFileDialog.getSaveFileName(self, "Save File", "", "*.spy") fil = open(filename , 'w') if(filename and self.edit_tab.tabText(self.edit_tab.currentIndex()) == "Python"): #Script hasn't been saved before and user specifies a valid filename self.edit_tab.setTabToolTip(self.edit_tab.currentIndex(), filename+'.spy') self.edit_tab.setTabText(self.edit_tab.currentIndex(), os.path.basename(str(filename+'.spy'))) else: #filename = self.edit_tab.tabText(self.edit_tab.currentIndex()) filename = self.edit_tab.tabToolTip(self.edit_tab.currentIndex()) fil = open( filename , 'w') fil.write(contents) fil.close() edit.setModified(False) @pyqtSlot() def on_actionOpen_triggered(self): filename = QFileDialog.getOpenFileName(self,"Open File","","*.spy") try: fil = open(filename , 'r') except IOError: return code = fil.read() edit = self.edit_tab.currentWidget() self.edit_tab.setTabText(self.edit_tab.currentIndex(), os.path.basename(str(filename))) self.edit_tab.setTabToolTip(self.edit_tab.currentIndex(), filename) edit.setPlainText(code) fil.close() @pyqtSlot() def on_actionRun_triggered(self): self.run() @pyqtSlot() def on_actionRunConsole_triggered(self): self.run(True) def run(self, console=False): edit = self.edit_tab.currentWidget() code = str(edit.toPlainText()) if isinstance(edit, PythonEditorWidget): self.py_console.attach() self.console_tab.setCurrentIndex(0) if console: namespace = self.py_console.namespace else: namespace = {} try: exec code in namespace except Exception as e: traceback.print_exc() try: Scripter.activeWindow.redraw = True Scripter.activeWindow.update() except: pass else: self.js_console.attach() self.console_tab.setCurrentIndex(1) if console: self.js_console.inter.execute(code) else: self.js_console.inter.execute_code(code)
class CodeEditorTabWidget(QWidget): """ Widget which representing code editor with tabs. """ def __init__(self, parent=None): QWidget.__init__(self, parent) vertical_layout = QVBoxLayout() self.tab_widget = QTabWidget() self.tab_widget.setTabBar(TabBar()) self.tab_widget.setTabsClosable(True) self.tab_widget.setUsesScrollButtons(True) self.tab_widget.setMovable(True) self.tab_widget.tabCloseRequested.connect(self.close_tab) self.tab_widget.currentChanged.connect(self.tab_changed) self.editor_status_bar = QStatusBar(self) self.editor_status_bar.setStyleSheet("QStatusBar{border-bottom: 1px outset grey; border-left: 1px outset grey; border-right: 1px outset grey;}") self.editor_status_bar.hide() vertical_layout.setSpacing(0) vertical_layout.setContentsMargins(5, 22, 0, 0) vertical_layout.addWidget(self.tab_widget) vertical_layout.addWidget(self.editor_status_bar) self.setLayout(vertical_layout) self.opened_tabs = 0 def open_file(self, file_path: str) -> None: """ Open file in new tab. Args: file_path(str): file path """ with open(file_path, 'r') as f: code_editor = CodeEditorWidget(self) code_editor.load_file(f) code_editor.file_saved = True code_editor.opened_file = f.name self.add_tab(code_editor, os.path.basename(f.name)) def new_file(self) -> None: """ Create new tab / file """ code_editor = CodeEditorWidget(self) code_editor.file_saved = False self.add_tab(code_editor, "NoName") def save_file(self, file_path: str = None) -> None: """ Save current file(as). Args: file_path(str): if file path is not None. Save as method called """ file = file_path or self.get_current_file() current_widget = self.get_current_widget() with open(file, 'w') as f: f.write(current_widget.get_plain_text()) current_widget.opened_file = file current_widget.file_saved = True self.tab_changed() self.set_tab_text(os.path.basename(file)) def add_tab(self, code_editor: CodeEditorWidget, file_name: str) -> None: """ Add new tab to the widget. Args: code_editor(CodeEditorWidget): code editor widget in new tab file_name(str): name of new tab - file name """ new_index = self.tab_widget.count() self.tab_widget.addTab(code_editor, file_name) self.tab_widget.setCurrentIndex(new_index) self.opened_tabs += 1 def tab_changed(self) -> None: """ Change hide/show information in editor status bar. Update line and column in main status bar. This method is called when currentChanged signal is emitted. """ current_widget = self.get_current_widget() if not current_widget: self.editor_status_bar.hide() return else: self.editor_status_bar.showMessage(self.get_current_file() or "File not saved") self.editor_status_bar.show() current_widget.main_window.set_new_cursor_position(current_widget.get_cursor()) def set_tab_text(self, text: str, index: int = None) -> None: """ Set new text of current tab Args: text(str): new text index(int): index of tab. If None -> use current """ current_index = index or self.tab_widget.currentIndex() self.tab_widget.setTabText(current_index, text) def close_tab(self, index: int) -> None: """ Close tab at index. Args: index(int): index of tab """ self.tab_widget.removeTab(index) self.opened_tabs -= 1 def is_file_saved(self) -> bool: """ Return if file in current widget is saved. Returns: bool: True if file is save else False """ current_tab = self.get_current_widget() return current_tab.file_saved def get_current_widget(self) -> CodeEditorWidget: """ Return widget in current active tab Returns: CodeEditorWidget: Code editor in current tab """ return self.tab_widget.currentWidget() def get_current_file(self) -> str: """ Return file path of file in current active tab Returns: str: file path of file in active tab """ return self.get_current_widget().opened_file
class TabDocker(QWidget): """ Tab widget for the settingswidgets. Inherits: QWidget """ sig_start_plot = pyqtSignal() latest_active = [None] def __init__(self, parent=None, **kwargs): """ Initialise layout for TabDocker Arguments: parent - Parent widget (default None) Return: None """ super(TabDocker, self).__init__(parent) self.parent = parent try: self.layout = kwargs['layout'] except KeyError: self.layout = None try: self.name = kwargs['name'] except KeyError: self.name = None self.widgets = [] layout_tmp = QVBoxLayout(self) self.parent_widget = QWidget(self) layout_tmp.addWidget(self.parent_widget) layout_tmp.setContentsMargins(0, 0, 0, 0) layout = QVBoxLayout(self.parent_widget) layout.setContentsMargins(0, 0, 0, 0) self.tab_widget = QTabWidget(self) if self.layout in ('TAB1', 'Settings'): tab_bar = MyTabBar(self.tab_widget) tab_bar.setObjectName('vertical') self.tab_widget.setObjectName('vertical') self.tab_widget.setTabBar(tab_bar) self.tab_widget.setTabPosition(QTabWidget.West) layout.addWidget(self.tab_widget) self.tab_widget.currentChanged.connect(self.assign_latest) @pyqtSlot(int) def assign_latest(self, idx): current_name = self.tab_widget.tabText(idx) try: parent_content = self.parent.content[self.layout].name except AttributeError: # Exception for the Default settings dialog parent_content = False except TypeError: # Exception for the Default settings dialog parent_content = False except KeyError: # Exception for the main window dialog parent_content = False check_list = (parent_content, self.name, current_name) latest_active = self for list_idx, entry in enumerate(check_list): if entry == 'Visualisation': cur_tab_widget = self.tab_widget.widget(idx) try: for i in range(list_idx): idx = cur_tab_widget.currentIndex() cur_tab_widget = cur_tab_widget.widget(idx) latest_active = cur_tab_widget if cur_tab_widget is not None else self except: latest_active = self break if self.latest_active[0] != latest_active: self.latest_active[0] = latest_active latest_active.sig_start_plot.emit() def setCurrentIndex(self, idx): """ Set the current Index of the tab_widget. Arguments: idx - Index to set Returns: Current index of self.tab_widget """ return self.tab_widget.setCurrentIndex(idx) def setCurrentWidget(self, widget): """ Set the current widget of the tab_widget. Arguments: idx - Widget to set Returns: Current index of self.tab_widget """ return self.tab_widget.setCurrentWidget(widget) def currentIndex(self): """ Get the current Index of the tab_widget. Returns: Current index of self.tab_widget """ return self.tab_widget.currentIndex() def add_tab(self, widget, name, add_widgets=True): """ Add a new tab to the TabDocker Arguments: widget - Widget to add name - Name of the widget Return: None """ if isinstance(widget, TabDocker): widget.parent_widget.setObjectName('tab') else: pass current_state = self.tab_widget.blockSignals(True) index = self.tab_widget.addTab(widget, name) if add_widgets: self.widgets.append(widget) self.tab_widget.blockSignals(current_state) self.tab_widget.setTabToolTip(index, name) def count(self): """ Return the number of tabs. Arguments: None Returns: Number of tabs """ return self.tab_widget.count() def widget(self, idx): """ Return the widget that belongs to the idx of tabs. Arguments: idx - Tab index Returns: Widget """ return self.tab_widget.widget(idx) def setMovable(self, status): """ Set the movable status for the tab widgets Arguments: status - Boolean variable for the status Returns: None """ return self.tab_widget.setMovable(status) def tabText(self, idx): """ Return the text of the tab at idx Arguments: idx - Index of the tab Returns: Text of the tab at position isx """ return self.tab_widget.tabText(idx) def setTabText(self, idx, text): """ Set the text for the tab at idx Arguments: idx - Index of the tab text - Text of the tab Returns: None """ return self.tab_widget.setTabText(idx, text) def removeTab(self, idx): """ Remove the widget located at tab idx Arguments: idx - Idx of the widget Returns: None """ current_state = self.tab_widget.blockSignals(True) idx = self.tab_widget.removeTab(idx) self.tab_widget.blockSignals(current_state) return idx def indexOf(self, widget): """ Get the index of the widget. Arguments: widget - Adress of the widget Returns: Index of the widget """ return self.tab_widget.indexOf(widget) def setTabPosition(self, position): """ Set the tab position of the Tab bar Arguments: position - Tab position as string ['North', 'East', 'West', 'South'] Returns: None """ tab_position_dict = { 'North': QTabWidget.North, 'South': QTabWidget.South, 'West': QTabWidget.West, 'East': QTabWidget.East, } self.tab_widget.setTabPosition(tab_position_dict[position]) def setTabEnabled(self, index, state): """ Set the tab position index to the enable state. Arguments: index - Tab position index state - State (True or False) Returns: None """ self.tab_widget.setTabEnabled(index, state) def order_tabs(self): current_state = self.tab_widget.blockSignals(True) widget_tuple = tuple([(self.widget(idx).name, self.widget(idx), self.tab_widget.isTabEnabled(idx)) for idx in range(self.count())]) for idx in reversed(range(self.count())): self.removeTab(idx) for idx, (name, widget, state) in enumerate(sorted(widget_tuple)): self.add_tab(widget, name, add_widgets=False) self.setTabEnabled(idx, state) if state: self.setCurrentIndex(idx) self.tab_widget.blockSignals(current_state) def enable_tab(self, visible): """ Enable or disable the tab. Arguments: visible - Enable if True, Disable if False name - Name of the tab to disable. Returns: None """ index = self.parent.content[self.layout].indexOf(self) if not visible: self.parent.content[self.layout].removeTab(index) else: self.parent.content[self.layout].add_tab(self, self.name) self.parent.content[self.layout].order_tabs()
class MainWindow(QDialog, window1.Ui_PyDialog): def __init__(self, parent=None): global arguments, return_keyword self.event_entered = False self.event2_entered = False super(MainWindow, self).__init__(parent) self.setupUi(self) if arguments.dontagain: from PyQt5.QtWidgets import QCheckBox self.dontagain_checkBox = QCheckBox(_("Don't show or ask this again."), self) self.verticalLayout.addWidget(self.dontagain_checkBox) if arguments.setdefault: from PyQt5.QtWidgets import QCheckBox self.dontagain_checkBox = QCheckBox(_("The selected set as default value."), self) self.verticalLayout.addWidget(self.dontagain_checkBox) if arguments.stayontop: from PyQt5.QtCore import Qt self.setWindowFlags(Qt.WindowStaysOnTopHint) self.label.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)) self.button_ids = ["details_button", "ok_button", "yes_button", "no_button", "continue_button", "save_button", "cancel_button"] self.button_names = { "details_button":_("Details"), "ok_button":_("Ok"), "yes_button":_("Yes"), "no_button":_("No"), "continue_button":_("Continue"), "save_button":_("Save"), "cancel_button":_("Cancel") } self.button_values = {} self.create_elements() self.word_wrap() if arguments.showsettings and not arguments.forkedprogressbar: self.toolButton.clicked.connect(self.settings_button_clicked) else: self.toolButton.hide() self.spacerItem1.changeSize(0, 0) def save_dontask(self, value): if arguments.dontagain and dontagain_available() and value != 2: if self.dontagain_checkBox.isChecked(): import configparser config = configparser.ConfigParser() config[config_section] = {} config[config_section][config_key] = value with open(config_file, 'w') as file: config.write(file) def save_default(self, value): if arguments.setdefault and setdefault_setdefault() and value != 2: if self.setdefault_checkBox.isChecked(): import configparser config = configparser.ConfigParser() config[config_section] = {} config[config_section][config_key] = value with open(config_file, 'w') as file: config.write(file) def word_wrap(self): if self.label.sizeHint().width() > 600: self.label.setWordWrap(True) self.label.setScaledContents(True) self.label.setMinimumWidth(600) def create_elements(self): self.active_buttons = dict((e, False) for e in self.button_names) self.progressbar_cancelled = False self.hide_unused_elements() self.init_conf() self.create_buttons() self.set_button_labels() # noab = len(list(filter(lambda x: self.active_buttons[x], self.active_buttons))) # self.reject_value = noab - 1 def hide_unused_elements(self): """ Hide the unused elements """ global arguments if not arguments.forkedprogressbar: self.progressBar.hide() if not arguments.slider: self.horizontalSlider.hide() if not arguments.combobox: self.comboBox.hide() if not arguments.inputbox and not arguments.password: self.lineEdit.hide() if not arguments.combobox and not arguments.password: self.label_2.hide() def init_conf(self): """ Initial configurations (buttons and labels) """ global arguments if arguments.title: self.setWindowTitle(pydialog_title) if arguments.icon: from PyQt5.QtGui import QIcon icon = QIcon(arguments.icon) self.setWindowIcon(icon) if arguments.yesno or arguments.warningyesno: self.enable_buttons(["yes_button", "no_button"]) if arguments.yesno: self.label.setText(arguments.yesno) else: self.label.setText(arguments.warningyesno) elif arguments.yesnocancel or arguments.warningyesnocancel: self.enable_buttons(["yes_button", "no_button", "cancel_button"]) if arguments.yesnocancel: self.label.setText(arguments.yesnocancel) else: self.label.setText(arguments.warningyesnocancel) elif arguments.sorry or arguments.error or arguments.msgbox or arguments.textbox or arguments.about: self.enable_buttons(["ok_button"]) if arguments.sorry: self.label.setText(arguments.sorry) elif arguments.error: print("itt", arguments.error) self.label.setText(arguments.error) elif arguments.msgbox: self.label.setText(arguments.msgbox) elif arguments.textbox: from PyQt5.QtWidgets import QTextBrowser width = 400 height = 250 url = arguments.textbox[0] if len(arguments.textbox) > 1: width = int(arguments.textbox[1]) if len(arguments.textbox) > 2: height = int(arguments.textbox[2]) self.textbrowser = QTextBrowser() self.textbrowser.setMinimumSize(width, height) self.verticalLayout_2.addWidget(self.textbrowser) file = open(url, "r") self.textbrowser.setText(file.read()) file.close() elif arguments.about: self.label.setText(""" <strong>Pydialog v{}</strong><br><br> Authors: <br><center><a href='mailto:[email protected]'>Miklos Horvath</a></center> <center><a href='mailto:[email protected]'>Charles Barcza</a></center><br> <center><a href='http://blackpantheros.eu'>blackPanther Europe</a></center>""".format(VERSION)) elif arguments.detailedsorry or arguments.detailederror: self.enable_buttons(["details_button", "ok_button"]) if arguments.detailedsorry: self.label.setText(arguments.detailedsorry[0]) self.details = arguments.detailedsorry[1] else: self.label.setText(arguments.detailederror[0]) self.details = arguments.detailederror[1] elif arguments.warningcontinuecancel: self.enable_buttons(["continue_button", "cancel_button"]) self.label.setText(arguments.warningcontinuecancel) elif arguments.forkedprogressbar: self.label.setText(arguments.forkedprogressbar[0]) if len(arguments.forkedprogressbar) > 1: self.progressBar.setMaximum(int(arguments.forkedprogressbar[1])) elif arguments.slider: self.enable_buttons(["ok_button", "cancel_button"]) self.label.setText(arguments.slider[0]) if len(arguments.slider) > 1: self.horizontalSlider.setMinimum(int(arguments.slider[1])) if len(arguments.slider) > 2: self.horizontalSlider.setMaximum(int(arguments.slider[2])) if len(arguments.slider) > 3: self.horizontalSlider.setSingleStep(int(arguments.slider[3])) self.horizontalSlider.setPageStep(int(arguments.slider[3])) elif arguments.inputbox: self.enable_buttons(["ok_button", "cancel_button"]) self.label.setText(arguments.inputbox[0]) if len(arguments.inputbox) > 1: self.lineEdit.setText(arguments.inputbox[1]) elif arguments.password: self.enable_buttons(["ok_button", "cancel_button"]) self.lineEdit.setEchoMode(2) self.label.setText(arguments.password[0]) self.label_2.setText(_("Password:"******"ok_button", "cancel_button"]) self.label.setText(arguments.combobox[0]) elif arguments.textinputbox: from PyQt5.QtWidgets import QTextEdit self.enable_buttons(["ok_button"]) self.textedit = QTextEdit() width = 400 height = 250 init_text = "" self.label.setText(arguments.textinputbox[0]) if len(arguments.textinputbox) > 1: init_text = arguments.textinputbox[1] if len(arguments.textinputbox) > 2: width = int(arguments.textinputbox[2]) if len(arguments.textinputbox) > 3: height = int(arguments.textinputbox[3]) self.textedit.setMinimumSize(width, height) self.verticalLayout_2.addWidget(self.textedit) self.textedit.setText(init_text) elif arguments.checklist or arguments.radiolist or arguments.menu: if arguments.checklist: scrollLayout, self.checkboxes = self.add_checkboxes() else: scrollLayout, self.buttonGroup, self.buttongroup_results = self.add_radiobuttons() #scrollAreaLayout, hscrollbar = self.create_scrollarea(scrollLayout) scrollAreaLayout = self.create_scrollarea(scrollLayout) if arguments.tab: from PyQt5.QtWidgets import QTabWidget, QVBoxLayout, QWidget if arguments.checklist: scrollLayout2, self.checkboxes2 = self.add_checkboxes(True) else: scrollLayout2, self.buttonGroup2, self.buttongroup_results2 = self.add_radiobuttons(True) #scrollAreaLayout2, hscrollbar2 = self.create_scrollarea(scrollLayout2) scrollAreaLayout2 = self.create_scrollarea(scrollLayout2) tab1 = QWidget() tab2 = QWidget() layout = QVBoxLayout(tab1) layout2 = QVBoxLayout(tab2) layout.addLayout(scrollAreaLayout) layout2.addLayout(scrollAreaLayout2) #layout.addWidget(hscrollbar) #layout2.addWidget(hscrollbar2) self.tabwidget = QTabWidget(self) self.tabwidget.addTab(tab1, arguments.tab[0]) self.tabwidget.addTab(tab2, arguments.tab[1]) self.verticalLayout_2.addWidget(self.tabwidget) else: self.verticalLayout_2.addLayout(scrollAreaLayout) #self.verticalLayout_2.addWidget(hscrollbar) if arguments.checklist: self.label.setText(arguments.checklist[0]) elif arguments.radiolist: self.label.setText(arguments.radiolist[0]) else: self.label.setText(arguments.menu[0]) self.enable_buttons(["ok_button", "cancel_button"]) def create_scrollarea(self, scrollLayout): from PyQt5.QtWidgets import QHBoxLayout, QWidget, QScrollArea #from PyQt5.QtCore import Qt scrollWidget = QWidget() scrollAreaLayout = QHBoxLayout() scrollWidget.setLayout(scrollLayout) #hscrollbar = QScrollBar() #vscrollbar = QScrollBar() scrollArea = QScrollArea() #scrollArea.setHorizontalScrollBar(hscrollbar) #scrollArea.setVerticalScrollBar(vscrollbar) #scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) #scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.set_scrollarea_height(scrollArea) scrollArea.setWidget(scrollWidget) scrollAreaLayout.addWidget(scrollArea) #scrollAreaLayout.addWidget(vscrollbar) return scrollAreaLayout #, hscrollbar def set_scrollarea_height(self, scrollarea): if arguments.checklist: elements = (len(arguments.checklist)-1) / 3 elif arguments.radiolist: elements = (len(arguments.radiolist)-1) / 3 elif arguments.menu: elements = (len(arguments.menu)-1) / 2 if elements < 3: pass elif elements == 3: scrollarea.setMinimumHeight(90) elif elements == 4: scrollarea.setMinimumHeight(115) else: scrollarea.setMinimumHeight(140) def add_checkboxes(self, tab=False): from PyQt5.QtWidgets import QCheckBox, QVBoxLayout scrollLayout = QVBoxLayout() checkboxes = [] if tab: i = 2 name = "tab" else: i = 1 name = "checklist" l = len(arguments.__dict__[name]) while i < l: checkbox = QCheckBox(arguments.__dict__[name][i+1]) if arguments.__dict__[name][i+2].lower() in ["true", "on"]: checkbox.setCheckState(2) checkboxes.append({"box":checkbox, "result":arguments.__dict__[name][i]}) scrollLayout.addWidget(checkbox) i += 3 return scrollLayout, checkboxes def add_radiobuttons(self, tab=False): from PyQt5.QtWidgets import QRadioButton, QButtonGroup, QVBoxLayout scrollLayout = QVBoxLayout() buttonGroup = QButtonGroup() buttongroup_results = {} i = 1 if tab: name = "tab" i = 2 elif arguments.radiolist: name = "radiolist" elif arguments.menu: name = "menu" arglen = len(arguments.__dict__[name]) while i < arglen: if arguments.radiolist: radiobutton = QRadioButton(arguments.__dict__[name][i+1]) buttongroup_results[radiobutton] = arguments.__dict__[name][i] try: if arguments.__dict__[name][i+2].lower() in ["true", "on"]: radiobutton.setChecked(True) except: break i += 3 else: radiobutton = QRadioButton(arguments.__dict__[name][i+1]) buttongroup_results[radiobutton] = arguments.__dict__[name][i] if i == 1: radiobutton.setChecked(True) if arguments.menu and arguments.default: if arguments.__dict__[name][i+1] == arguments.default: radiobutton.setChecked(True) i += 2 scrollLayout.addWidget(radiobutton) buttonGroup.addButton(radiobutton) return scrollLayout, buttonGroup, buttongroup_results def set_button_labels(self): """Set the button labels""" global arguments if arguments.yeslabel and self.active_buttons["yes_button"]: self.buttons["yes_button"].setText(arguments.yeslabel) if arguments.nolabel and self.active_buttons["no_button"]: self.buttons["no_button"].setText(arguments.nolabel) if arguments.cancellabel and self.active_buttons["cancel_button"]: self.buttons["cancel_button"].setText(arguments.cancellabel) if arguments.continuelabel and self.active_buttons["continue_button"]: self.buttons["continue_button"].setText(arguments.continuelabel) def create_buttons(self): global arguments self.buttons = {} i = 0 for button_id in self.button_ids: if self.active_buttons[button_id]: self.buttons[button_id] = QPushButton(self.button_names[button_id]) self.horizontalLayout.addWidget(self.buttons[button_id]) if button_id == "details_button": self.buttons["details_button"].clicked.connect(self.details_button_clicked) # elif button_id == "cancel_button": # self.buttons[button_id].clicked.connect(self.reject) else: self.button_values[button_id] = i exec("self.buttons[button_id].clicked.connect(self."+button_id+"_clicked)") i += 1 def print_checkboxes(self): if arguments.separateoutput: fs = '{}' data_end = "\n" else: fs = '"{}"' data_end = " " for e in self.checkboxes: if e["box"].isChecked(): print(fs.format(e["result"]), end=data_end) if arguments.tab: for e in self.checkboxes2: if e["box"].isChecked(): print(fs.format(e["result"]), end=data_end) def get_checked_radiobutton(self): n = "" if arguments.tab: if self.tabwidget.currentIndex() == 1: n = "2" radiobutton_name = self.__dict__["buttonGroup"+n].checkedButton() print(self.__dict__["buttongroup_results"+n][radiobutton_name]) def get_combo_text(self): print(self.comboBox.currentText()) def ok_button_clicked(self): if arguments.slider: print(self.horizontalSlider.value()) elif arguments.inputbox or arguments.password: print(self.lineEdit.text()) elif arguments.checklist: self.print_checkboxes() elif arguments.radiolist or arguments.menu: self.get_checked_radiobutton() elif arguments.combobox: self.get_combo_text() elif arguments.textinputbox: print(self.textedit.toPlainText()) value = str(self.button_values["ok_button"]) print(return_keyword+str(self.button_values["ok_button"])+">") self.save_dontask(value) self.done(0) def yes_button_clicked(self): value = str(self.button_values["yes_button"]) print(return_keyword+value+">") self.save_dontask(value) self.done(0) def no_button_clicked(self): value = str(self.button_values["no_button"]) print(return_keyword+value+">") self.save_dontask(value) self.done(0) def continue_button_clicked(self): value = str(self.button_values["continue_button"]) print(return_keyword+value+">") self.save_dontask(value) self.done(0) def save_button_clicked(self): print(return_keyword+str(self.button_values["save_button"])+">") self.done(0) def cancel_button_clicked(self): print(return_keyword+str(self.button_values["cancel_button"])+">") self.done(0) def settings_button_clicked(self): print(return_keyword+"100>") self.done(0) def reject(self): # value = str(self.reject_value) # print(return_keyword+value+">") print(return_keyword+">") self.done(0) def enable_buttons (self, button_list): for button in button_list: self.active_buttons[button] = True def details_button_clicked (self): self.label.setText(self.label.text() + '\n\n' + self.details) self.buttons["details_button"].setDisabled(True) def progressbar_cancel_clicked(self): self.progressbar_cancelled = True def showCancelButton(self): if not "cancel_button" in self.buttons: self.buttons["cancel_button"] = QPushButton(self.button_names["cancel_button"]) self.buttons["cancel_button"].clicked.connect(self.progressbar_cancel_clicked) self.horizontalLayout.addWidget(self.buttons["cancel_button"]) self.progressbar_cancelled = False self.buttons["cancel_button"].show()
class RobotBeautifyWindow(QWidget): def __init__(self): super(RobotBeautifyWindow, self).__init__() self.setWindowFlag(Qt.FramelessWindowHint) self.setObjectName('mainW') self.deskTop = QDesktopWidget().availableGeometry() self.setFixedSize(self.deskTop.width(), self.deskTop.height()) self.setWindowIcon(QIcon('./images/logo.png')) self.maxWidth = self.deskTop.width() # self.warningWindow = QWidget() # self.warningWindow.show() """init file params""" self.openFilePath = '' self.saveFilePath = '' self.filesDict = {} self.editors = {} """init some params""" self.warningNum = 1 self.setupUi() self.showMaximized() def setupUi(self): self.createTitle() self.createToolsBar() # self.createFilesListArea() self.createFileDetailArea() self.createStatusBar() self.titleLabel.doubleClicked.connect(lambda: self.move(0, 0)) self.closeBtn.clicked.connect(self.close) # self.maxBtn.clicked.connect(self.show_start) self.minBtn.clicked.connect(self.showMinimized) self.newBtn.clicked.connect(self.__addNewTab) self.openBtn.clicked.connect(self.__openFile) self.saveBtn.clicked.connect(self.__saveFile) self.beautifyBtn.clicked.connect(self.__formatContent) self.warningBtn.clicked.connect(self.__changeLight) def __changeLight(self): self.__initLightPos() self.lightBtn = [self.redBtn, self.yellowBtn, self.greenBtn] if self.warningNum == 0: index = 2 elif self.warningNum <= 10: index = 1 else: index = 0 self.lightBtn[index].move(880 + 55 * index, 10) self.lightBtn[index].resize(40, 40) self.lightBtn[index].setStyleSheet('border-radius: 20px;') if self.warningNum > 10: self.warningNum = 0 else: self.warningNum += 5 def __initLightPos(self): for index, btn in enumerate( [self.redBtn, self.yellowBtn, self.greenBtn]): btn.move(890 + 55 * index, 20) btn.resize(20, 20) btn.setStyleSheet('border-radius: 10px;') def createTitle(self): self.titleLabel = MoveLabel(self) self.titleLabel.resize(self.maxWidth, 60) self.titleLabel.setProperty('class', 'titleLabel') self.titleLabel.setText("Robot Beautify") self.logoLabel = QLabel(self) self.logoLabel.resize(30, 30) self.logoLabel.setObjectName('logoLabel') self.logoLabel.move(20, 15) self.redBtn = LightBtn('red', self) self.redBtn.move(890, 20) self.yellowBtn = LightBtn('yellow', self) self.yellowBtn.move(945, 20) self.greenBtn = LightBtn('green', self) self.greenBtn.move(1000, 20) # close button self.closeBtn = WinBtn(self) self.closeBtn.move(self.maxWidth - self.closeBtn.width(), 0) self.closeBtn.setIcon(QIcon("./images/close.png")) # minimum button self.minBtn = WinBtn(self) self.minBtn.move( self.maxWidth - self.closeBtn.width() - self.minBtn.width(), 0) self.minBtn.setIcon(QIcon("./images/min.png")) def createToolsBar(self): self.toolsBar = QWidget(self) self.toolsBar.setFixedSize(60, 970) self.toolsBar.move(0, 60) self.toolsBar.setObjectName('toolsBarW') self.newBtn = ToolBarBtn('+', self.toolsBar) self.openBtn = ToolBarBtn('O', self.toolsBar) self.saveBtn = ToolBarBtn('S', self.toolsBar) self.beautifyBtn = ToolBarBtn('B', self.toolsBar) self.warningBtn = ToolBarBtn('W', self.toolsBar) self.newBtn.setShortcut('ctrl+n') # no need space self.openBtn.setShortcut('ctrl+o') self.saveBtn.setShortcut('ctrl+s') self.beautifyBtn.setShortcut('ctrl+b') self.warningBtn.setShortcut('ctrl+w') self.newBtn.move(10, 10) self.openBtn.move(10, 60) self.saveBtn.move(10, 110) self.beautifyBtn.move(10, 160) self.warningBtn.move(10, 210) # def createFilesListArea(self): # self.filesListArea = QWidget(self) # self.filesListArea.resize(200, 1020) # self.filesListArea.move(60, 60) # self.filesListArea.setObjectName('filesListArea') def createFileDetailArea(self): self.fileDetailArea = QWidget(self) self.fileDetailArea.resize(1860, 940) self.fileDetailArea.move(60, 60) self.fileDetailArea.setObjectName('editW') self.tabs = QTabWidget(self.fileDetailArea) self.tabs.resize(self.tabs.parent().size()) self.tabs.setTabShape(QTabWidget.Rounded) # self.tabs.setDocumentMode(True) # self.tabs.setMovable(True) self.tabs.setTabsClosable(True) self.tabs.setProperty('class', 'tab') defaultEditor = QCodeEditor() self.editors['untitled'] = [ defaultEditor, RobotHighLighter(defaultEditor.document()) ] self.tabs.addTab(defaultEditor, 'untitled') self.tabs.currentChanged.connect(self.__tabChanged) self.tabs.tabCloseRequested.connect(self.__tabClosed) def createStatusBar(self): self.statusBar = QLabel(self) self.statusBar.setFixedSize(self.width(), 30) self.statusBar.setObjectName('statusBar') self.statusBar.move(0, self.height() - self.statusBar.height()) def __checkContent(self): text = self.tabs.currentWidget().toPlainText() if not text: return None self.rc = RobotCheck( text ) # must exit as an instant property to keep alive after function end self.rc.done.connect(self.__checkDone) self.rc.start() def __checkDone(self, warnings): print(warnings) def __formatContent(self): text = self.tabs.currentWidget().toPlainText() if not text: return None self.fc = RobotFormatter(text) self.fc.done.connect(self.__formatDone) self.fc.start() def __formatDone(self, content): self.editors[self.tabs.tabText( self.tabs.currentIndex())][0].setPlainText(content) def __addNewTab(self): if 'untitled' in self.editors.keys(): self.statusBar.setText( 'Please save untitled file before create a new one...') return None defaultEditor = QCodeEditor() self.editors['untitled'] = [ defaultEditor, RobotHighLighter(defaultEditor.document()) ] self.tabs.addTab(defaultEditor, 'untitled') self.tabs.setCurrentIndex(self.tabs.count() - 1) def __tabClosed(self, index): count = self.tabs.count() key = self.tabs.tabText(index) self.tabs.removeTab(index) del self.editors[key] if count == 1: self.__addNewTab() def __tabChanged(self, index): self.statusBar.setText(self.filesDict.get(self.tabs.tabText(index), '')) def __openFile(self): if not self.openFilePath: file = QFileDialog().getOpenFileName(parent=self, caption='Select File', directory=os.path.join( os.path.expanduser("~"), 'Desktop'), filter='Robot Files(*.robot)') else: file = QFileDialog().getOpenFileName(self, caption='Select File', directory=self.openFilePath, filter='Robot Files(*.robot)') if file[0]: self.openFilePath = os.path.dirname(file[0]) tmp = self.tabs.tabText(self.tabs.currentIndex()) key = file[0].split('/')[-1] self.filesDict[key] = file[0] if not tmp.endswith('.robot'): # untitled self.editors[key] = self.editors.get(tmp) del self.editors[tmp] self.tabs.setTabText(self.tabs.currentIndex(), key) else: editor = QCodeEditor() self.editors[key] = [ editor, RobotHighLighter(editor.document()) ] self.tabs.insertTab(self.tabs.count(), editor, key) self.tabs.setCurrentIndex(self.tabs.count() - 1) self.__tabChanged(self.tabs.currentIndex()) with open(file[0], 'r') as f: for line in f.readlines(): self.editors.get(key)[0].appendPlainText(line.strip('\n')) def __saveFile(self): key = self.tabs.tabText(self.tabs.currentIndex()) if key == 'untitled': if not self.saveFilePath: file = QFileDialog().getSaveFileName( parent=self, caption='Save File', directory=os.path.join(os.path.expanduser("~"), 'untitled'), filter='Robot Files(*.robot)') else: file = QFileDialog().getSaveFileName( self, caption='Save File', directory=self.saveFilePath, filter='Robot Files(*.robot)') if file[0]: self.saveFilePath = os.path.dirname(file[0]) print('saveFilePath:', self.saveFilePath) fileName = file[0] key = file[0].split('/')[-1] self.filesDict[key] = file[0] self.editors[key] = self.editors.get('untitled') del self.editors['untitled'] self.tabs.setTabText(self.tabs.currentIndex(), key) self.__tabChanged(self.tabs.currentIndex()) else: fileName = '' else: fileName = self.filesDict.get(key) if fileName: with open(fileName, 'w') as f: text = self.tabs.currentWidget().toPlainText() print(text) f.write(text) self.__checkContent()
class ImageTransformCtrlWidget(_AbstractCtrlWidget): """Control widget for image transform in the ImageTool.""" extract_concentric_rings_sgn = pyqtSignal() transform_type_changed_sgn = pyqtSignal(int) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self._ma_window_le = SmartLineEdit("1") validator = QIntValidator() validator.setBottom(1) self._ma_window_le.setValidator(validator) self._concentric_rings = _ConcentricRingsCtrlWidget() self._fourier_transform = _FourierTransformCtrlWidget() self._edge_detection = _EdgeDetectionCtrlWidget() self._opt_tab = QTabWidget() self._opt_tab.addTab(self._concentric_rings, "Concentric rings") self._opt_tab.addTab(self._fourier_transform, "Fourier transform") self._opt_tab.addTab(self._edge_detection, "Edge detection") self._non_reconfigurable_widgets = [ self._concentric_rings.detect_btn, ] self.initUI() self.initConnections() def initUI(self): """Override.""" common = QFrame() common_layout = QGridLayout() common_layout.addWidget(QLabel("Moving average window: "), 0, 0) common_layout.addWidget(self._ma_window_le, 0, 1) common.setLayout(common_layout) layout = QHBoxLayout() layout.addWidget(common) layout.addWidget(self._opt_tab) self.setLayout(layout) self.setFixedHeight(self.minimumSizeHint().height()) def initConnections(self): """Override.""" mediator = self._mediator self._ma_window_le.value_changed_sgn.connect( mediator.onItMaWindowChange) self._opt_tab.currentChanged.connect(mediator.onItTransformTypeChange) self._opt_tab.currentChanged.connect(self.transform_type_changed_sgn) cr = self._concentric_rings cr.detect_btn.clicked.connect(self.extract_concentric_rings_sgn) cr.cx_le.value_changed_sgn.connect(mediator.onItCrCxChange) cr.cy_le.value_changed_sgn.connect(mediator.onItCrCyChange) cr.prominence_le.value_changed_sgn.connect( mediator.onItCrProminenceChange) cr.distance_le.value_changed_sgn.connect(mediator.onItCrDistanceChange) cr.min_count_le.value_changed_sgn.connect( mediator.onItCrMinCountChange) fft = self._fourier_transform fft.logrithmic_cb.toggled.connect( mediator.onItFftLogrithmicScaleChange) ed = self._edge_detection ed.kernel_size_sp.valueChanged.connect(mediator.onItEdKernelSizeChange) ed.sigma_sp.valueChanged.connect(mediator.onItEdSigmaChange) ed.threshold_le.value_changed_sgn.connect( mediator.onItEdThresholdChange) def updateMetaData(self): """Override.""" self._ma_window_le.returnPressed.emit() if self.isVisible(): self.registerTransformType() else: self.unregisterTransformType() cr = self._concentric_rings cr.cx_le.returnPressed.emit() cr.cy_le.returnPressed.emit() cr.prominence_le.returnPressed.emit() cr.distance_le.returnPressed.emit() cr.min_count_le.returnPressed.emit() fft = self._fourier_transform fft.logrithmic_cb.toggled.emit(fft.logrithmic_cb.isChecked()) ed = self._edge_detection ed.kernel_size_sp.valueChanged.emit(ed.kernel_size_sp.value()) ed.sigma_sp.valueChanged.emit(ed.sigma_sp.value()) ed.threshold_le.returnPressed.emit() return True def loadMetaData(self): """Override.""" cfg = self._meta.hget_all(mt.IMAGE_TRANSFORM_PROC) self._updateWidgetValue(self._ma_window_le, cfg, "ma_window") # do not load transform type since it is not an "input" cr = self._concentric_rings self._updateWidgetValue(cr.cx_le, cfg, "cr:cx") self._updateWidgetValue(cr.cy_le, cfg, "cr:cy") self._updateWidgetValue(cr.prominence_le, cfg, "cr:prominence") self._updateWidgetValue(cr.distance_le, cfg, "cr:distance") self._updateWidgetValue(cr.min_count_le, cfg, "cr:min_count") fft = self._fourier_transform self._updateWidgetValue(fft.logrithmic_cb, cfg, "fft:logrithmic") ed = self._edge_detection self._updateWidgetValue(ed.kernel_size_sp, cfg, "ed:kernel_size", cast=int) self._updateWidgetValue(ed.sigma_sp, cfg, "ed:sigma", cast=float) self._updateWidgetValue(ed.threshold_le, cfg, "ed:threshold") def registerTransformType(self): self._opt_tab.currentChanged.emit(self._opt_tab.currentIndex()) def unregisterTransformType(self): self._mediator.onItTransformTypeChange( int(ImageTransformType.UNDEFINED)) def extractFeature(self, img): return self._opt_tab.currentWidget().extractFeature(img)
class Calculatormainwindow(QWidget): """ 计算器主界面窗口 """ _startPos = None _endPos = None _isTracking = None def __init__(self): super(Calculatormainwindow, self).__init__() self.init() self.setup_ui() self.load_qss() def init(self): self.setWindowFlags(Qt.FramelessWindowHint) # 设置无边框 self.setAttribute(Qt.WA_TranslucentBackground, True) # 将form设置为透明 self.setMinimumHeight(Const.MINIMUMHEIGHT) # 设置窗体最小高度 self.setMinimumWidth(Const.MINIMUMWIDTH) # 设置窗体最小宽度 self.setWindowTitle('计算器') self.setWindowIcon(QIcon("./image/calculator.jpg")) def setup_ui(self): # 布局 all_layout = QVBoxLayout() all_layout.setObjectName('all_layout') all_layout.setSpacing(Const.SET_SPACING) self.setLayout(all_layout) # 调整布局与边界距离 all_layout.setContentsMargins(Const.SET_CONTENTSMARGINS, Const.SET_CONTENTSMARGINS, Const.SET_CONTENTSMARGINS, Const.SET_CONTENTSMARGINS) # 调用自定义标题栏 self.title = CommonhelperTitleBar() self.title.setObjectName('title') self.title.spinnerBtn = QPushButton(self.title) # 创建下拉列表按钮 self.title.spinnerBtn.setObjectName("spinnerBtn") self.title.spinnerBtn.resize(40, 40) self.title.spinnerBtn.setIconSize(QSize(30, 30)) self.title.spinnerBtn.setIcon(QIcon(Const.SPINNERBTN_ICON)) self.title.spinnerBtn.move(60, 0) self.title.spinnerBtn.setCursor(QCursor(Qt.PointingHandCursor)) self.title.spinnerBtn.setToolTip("功能列表") self.title.spinnerBtn.clicked.connect(self.spinner_btn_pressed) # 自定义最小化、关闭按钮槽函数连接 self.title.minButton.clicked.connect(self.show_mininized_window) self.title.closeButton.clicked.connect(self.close_window) # 隐藏复原和最大化按钮(不可用) self.title.restoreButton.setHidden(True) self.title.maxButton.setHidden(True) # 自定义标题Icon和内容 title_icon = QPixmap("./image/calculator.jpg") self.title.iconLabel.setPixmap(title_icon.scaled(40, 40)) self.title.titleLabel.setText('标准计算器') self.title.titleLabel.setFont(QFont("STSong", 16)) # 华文宋体 self.title.titleLabel.resize(40, 40) # 界面主窗口 self.centerWidget = QTabWidget() self.centerWidget.setObjectName('centerWidget') all_layout.addWidget(self.title) all_layout.addWidget(self.centerWidget) # 下拉列表 self.spinner() # 加载窗口 self.show_win() # ************************** 业务逻辑 ************************* def load_qss(self): """ 加载样式表 :return: """ style_file = './Window/childWindow/calculatorWindow/Qss/CalculatorMainWindow.qss' qss_style = CommonhelperQss.read_qss(style_file) self.setStyleSheet(qss_style) def show_win(self): """ 显示默认窗口你(高级计算器窗口) :return: """ self.standardCalculator = Standardcalculator() self.scienceCalculator = Sciencecalculator() self.baseConversionWindow = Uiconversionofnumbersystems() self.centerWidget.addTab(self.standardCalculator, "标准计算器") self.centerWidget.addTab(self.scienceCalculator, "科学计算器") self.centerWidget.addTab(self.baseConversionWindow, "程序员计算器") def paintEvent(self, event): """ 重写paintEvent,实现边框阴影 :param event: :return: """ path = QPainterPath() path.setFillRule(Qt.WindingFill) path.addRect(10, 10, self.width() - 20, self.height() - 20) painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing, True) painter.fillPath(path, QBrush(Qt.white)) color = QColor(0, 0, 0, 50) for i in range(0, 10): path = QPainterPath() path.setFillRule(Qt.WindingFill) path.addRect(10 - i, 10 - i, self.width() - (10 - i) * 2, self.height() - (10 - i) * 2) color.setAlpha(150 - math.sqrt(i) * 50) painter.setPen(color) painter.drawPath(path) # #########################自定义下拉列表 #######################开始 # 自定义下拉列表,实现几种计算界面之间的切换 def spinner(self): """ 表自定义下拉列表 :return: """ # 创建下拉列表框 self.spinnerQwidget = QWidget(self) self.spinnerQwidget.setObjectName('spinnerQwidget') self.spinnerQwidget.setGeometry(70, 50, 250, 250) self.spinnerQwidget.setStyleSheet('background-color:white;') self.spinnerQwidget.hide() self.spinnerLayout = QVBoxLayout(self.spinnerQwidget) self.spinnerLayout.setObjectName("spinnerLayout") # 标准计算器切换按钮 self.StandardCalculatorBtn = QPushButton("标准计算器") self.StandardCalculatorBtn.setObjectName("calculatorBtn") self.StandardCalculatorBtn.clicked.connect( lambda: self.calculator_win_change(1)) # 科学计算器切换按钮 self.scienceCalculatorBtn = QPushButton("科学计算器") self.scienceCalculatorBtn.setObjectName("scienceCalculatorBtn") self.scienceCalculatorBtn.clicked.connect( lambda: self.calculator_win_change(2)) # 程序员计算器切换按钮 self.baseConversionBtn = QPushButton("程序员计算器") self.baseConversionBtn.setObjectName("baseConversionBtn") self.baseConversionBtn.clicked.connect( lambda: self.calculator_win_change(3)) self.StandardCalculatorBtn.setStyleSheet("background-color:gray;") self.baseConversionBtn.setStyleSheet("background-color:gray;") self.spinnerQuitBtn = QPushButton(self.spinnerQwidget) self.spinnerQuitBtn.setObjectName("spinnerQuitBtn") self.spinnerQuitBtn.setIcon(QIcon("./image/spinnerQuit.png")) self.spinnerQuitBtn.setStyleSheet("background-color:cyan;") self.spinnerQuitBtn.clicked.connect(lambda: self.spinner_btn_pressed()) # 分隔控件的空白窗口 self.separator = QWidget(self.spinnerQwidget) self.separator.setObjectName("separator") self.separator.resize(150, 20) # 往下拉列表框添加控件 self.spinnerLayout.addWidget(self.StandardCalculatorBtn) self.spinnerLayout.addWidget(self.scienceCalculatorBtn) self.spinnerLayout.addWidget(self.baseConversionBtn) self.spinnerLayout.addWidget(self.separator) self.spinnerLayout.addWidget(self.spinnerQuitBtn) # #########################自定义下拉列表 #######################结束 # ***************************** 业务逻辑 ******************************** # ####################设置窗口大小槽函数####################开始 def show_mininized_window(self): """ 最小化窗口 :return: """ self.showMinimized() # # 最大化窗口 # def show_maximized_window(self): # ''' # # :return: # ''' # self.showMaximized() # # 复原窗口 # def show_restore_window(self): # ''' # # :return: # ''' # if self.isMaximized(): # self.showNormal() # else: # self.showMaximized() def close_window(self): """ 关闭窗口 :return: """ self.close() def set_title(self, _str): """ 设置状态信息 :param _str: :return: """ self.titleLabel.set_text(_str) def set_icon(self, pix): """ 设置icon :param pix: :return: """ self.iconLabel.setPixmap( pix.scaled(self.iconLabel.size() - QSize(Const.TITLE_ICON_MAG, Const.TITLE_ICON_MAG))) # ####################设置窗口大小槽函数####################结束 # #########################calculator窗口切换#######################开始 def calculator_win_change(self, flag): """ 窗口切换 :param flag: :return: """ self.flag = flag if self.flag == 1: if self.centerWidget.currentIndex() == self.flag - 1: QMessageBox.information(self.centerWidget, "提示", "已经为当前窗口!", QMessageBox.Ok) else: self.centerWidget.setCurrentIndex(self.flag - 1) self.title.titleLabel.setText('标准计算器') elif self.flag == 2: if self.centerWidget.currentIndex() == self.flag - 1: QMessageBox.information(self.centerWidget, "提示", "已经为当前窗口!", QMessageBox.Ok) else: self.centerWidget.setCurrentIndex(self.flag - 1) self.title.titleLabel.setText('科学计算器') elif self.flag == 3: if self.centerWidget.currentIndex() == self.flag - 1: QMessageBox.information(self.centerWidget, "提示", "已经为当前窗口!", QMessageBox.Ok) else: self.centerWidget.setCurrentIndex(self.flag - 1) self.title.titleLabel.setText('程序员计算器') # #########################calculator窗口切换#######################结束 # #########################实现窗口拖动#######################开始 # 重写鼠标事件 def mousePressEvent(self, e: QMouseEvent): """ 鼠标点击事件 :param e: :return: """ if e.button() == Qt.LeftButton: self._isTracking = True self._starPos = QPoint(e.x(), e.y()) def mouseMoveEvent(self, e: QMouseEvent): """ 鼠标移动事件 :param e: :return: """ self._endPos = e.pos() - self._starPos self.move(self.pos() + self._endPos) def mouseReleaseEvent(self, e: QMouseEvent): """ 鼠标释放事件 :param e: :return: """ if e.button() == Qt.LeftButton: self._isTracking = False self._starPos = None self._endPos = None # #########################实现窗口拖动#######################结束 def spinner_btn_pressed(self): """ 下拉列表槽函数 :return: """ if self.spinnerQwidget.isHidden(): self.spinnerQwidget.show() else: self.spinnerQwidget.hide() def spinner_quit_btn_pressed(self): """ 隐藏下拉列表按钮槽函数 :return: """ self.spinnerQwidget.hide()
class MainWindows(QWidget): ## Main class 4 tabs : cameras def __init__(self): super().__init__() self.left = 100 self.top = 30 self.width = 1400 self.height = 1700 self.setGeometry(self.left, self.top, self.width, self.height) self.setWindowTitle('Lolita Cameras') self.setStyleSheet(qdarkstyle.load_stylesheet_pyqt5()) p = pathlib.Path(__file__) sepa = os.sep self.icon = str(p.parent) + sepa + 'icons' + sepa self.setWindowIcon(QIcon(self.icon + 'LOA.png')) self.setup() self.actionButton() def setup(self): self.layout = QVBoxLayout(self) self.layout.setContentsMargins(1, 1, 1, 1) self.setContentsMargins(1, 1, 1, 1) self.tabs = QTabWidget() self.tabs.setContentsMargins(1, 1, 1, 1) pathVisu = 'C:/Users/loa/Desktop/Python/camera/confCamera.ini' self.tab0 = CameraMotorLoop.CAMERAMOTOR(cam="cam2", fft='off', meas='on', affLight=False, aff='left', separate=False, multi=False, confpath=pathVisu, loop=True) self.tab1 = camera2.CAMERA(cam="cam3", fft='off', meas='on', affLight=False, aff='left', separate=False, multi=False, confpath=pathVisu) self.tab2 = camera2.CAMERA(cam="cam1", fft='off', meas='on', affLight=False, aff='left', separate=False, multi=False, confpath=pathVisu) self.tab3 = camera2.CAMERA(cam='cam4', fft='off', meas='on', affLight=False, aff='left', separate=False, multi=False, confpath=pathVisu) self.tab4 = CameraMotorLoop.CAMERAMOTOR(cam='cam5', fft='off', meas='on', affLight=False, aff='left', separate=False, multi=False, confpath=pathVisu, loop=True) # self.tab2=App4Cam() self.tabs.addTab(self.tab0, self.tab0.ccdName) self.tabs.addTab(self.tab1, self.tab1.ccdName) self.tabs.addTab(self.tab2, self.tab2.ccdName) self.tabs.addTab(self.tab3, self.tab3.ccdName) self.tabs.addTab(self.tab4, self.tab4.ccdName) #self.tabs.addTab(self.tab2,' P3 ') self.layout.addWidget(self.tabs) self.setLayout(self.layout) def changeTab(self): print('tab change', 'tab is', self.tabs.currentIndex()) # self.tab=[self.tab0,self.tab1] # self.tab0.stopRun() # self.tab1.stopRun() #self.tab2.stopRun() def actionButton(self): self.tabs.currentChanged.connect(self.changeTab) def closeEvent(self, event): # exit event.accept()
class Window(QMainWindow): def __init__(self): super().__init__() self.comics_tabs = {} self.comics_tabs_index = {} self.comics_pictures = {} self.comics_current_page = {} self.comics_picture_label = {} self.setGeometry(50, 50, 850, 600) self.setWindowTitle('Comics') self.init_GUI() def init_GUI(self): # create toolbar self.toolbar = QToolBar('Fonctionnalités') self.addToolBar(self.toolbar) self.toolbar.setIconSize(QSize(32, 32)) # add "add a comic" button to toolbar self.add_action = QAction( QIcon('icons/add.png'), 'Nouvelle bande déssinée', self) self.add_action.setStatusTip('Ajouter une nouvelle bande déssinée') self.add_action.triggered.connect(add_comic) self.add_action.setShortcut(QKeySequence('Ctrl+N')) self.toolbar.addAction(self.add_action) # add "previous" button to toolbar self.previous_action = QAction( QIcon('icons/previous.png'), 'Page précédente', self) self.previous_action.setStatusTip('Passer à la page précédente') self.previous_action.triggered.connect(self.previous_page) self.previous_action.setShortcut(QKeySequence('Ctrl+L')) self.toolbar.addAction(self.previous_action) # add "next" button to toolbar self.next_action = QAction( QIcon('icons/next.png'), 'Page suivante', self) self.next_action.setStatusTip('Passer à la page suivante') self.next_action.triggered.connect(self.next_page) self.next_action.setShortcut(QKeySequence('Ctrl+O')) self.toolbar.addAction(self.next_action) # add main view self.centralwidget = QWidget(self) self.centralwidget.setObjectName('centralwidget') self.setCentralWidget(self.centralwidget) # add tabs widget self.tab_widget = QTabWidget(self.centralwidget) self.tab_widget.setGeometry(QRect(0, 0, 850, 600)) self.tab_widget.setObjectName('tab_widget') self.tab_widget.setTabsClosable(False) self.tab_widget.tabCloseRequested.connect(lambda index: self.remove_tab(index)) # add library tab self.library_tab = QWidget() self.library_tab.setObjectName('library_tab') self.tab_widget.addTab(self.library_tab, 'Bibliothèque') # show library in library tab self.scroll_area = QScrollArea(self.library_tab) self.scroll_area.setGeometry(QRect(0, 0, 850, 600)) self.scroll_area.setWidgetResizable(True) self.scroll_area.setObjectName('scroll_area') self.scroll_area_widget_contents = QWidget() self.scroll_area_widget_contents.setGeometry(QRect(0, 0, 850, 600)) self.scroll_area_widget_contents.setObjectName( 'scroll_area_widget_contents') self.scroll_area.setWidget(self.scroll_area_widget_contents) self.table_widget = QTableWidget(self.scroll_area_widget_contents) self.table_widget.setGeometry(QRect(0, 0, 850, 600)) self.table_widget.setRowCount(1) self.table_widget.setColumnCount(8) self.table_widget.setObjectName('table_widget') self.table_widget.setEditTriggers(QAbstractItemView.NoEditTriggers) self.set_library() ### actions ### def previous_page(self): if not self.tab_widget.currentIndex() == 0: current_comics = None for key, value in self.comics_tabs.items(): if value == self.tab_widget.currentWidget(): current_comics = key if self.comics_current_page[current_comics] >= 1: self.comics_current_page[current_comics] -= 1 page = self.comics_current_page[current_comics] self.comics_picture_label[current_comics].setPixmap(QPixmap( 'library/' + current_comics + '/' + self.comics_pictures[current_comics][page])) def next_page(self): if not self.tab_widget.currentIndex() == 0: current_comics = None for key, value in self.comics_tabs.items(): if value == self.tab_widget.currentWidget(): current_comics = key if self.comics_current_page[current_comics] < len(self.comics_pictures[current_comics]) - 1: self.comics_current_page[current_comics] += 1 page = self.comics_current_page[current_comics] self.comics_picture_label[current_comics].setPixmap(QPixmap( 'library/' + current_comics + '/' + self.comics_pictures[current_comics][page])) def make_read_comic(self, comic): def read_comic(): comics_title = os.path.splitext(comic)[0] if not comics_title in self.comics_tabs_index: self.comics_tabs[comics_title] = QWidget() self.comics_tabs[comics_title].setObjectName('comic_view_tab') self.tab_widget.addTab( self.comics_tabs[comics_title], comics_title) self.comics_tabs_index[comics_title] = self.tab_widget.indexOf(self.comics_tabs[comics_title]) self.comics_picture_label[comics_title] = QLabel( self.comics_tabs[comics_title]) self.comics_picture_label[comics_title].setGeometry( QRect(0, 0, 350, 500)) self.comics_picture_label[comics_title].setScaledContents(True) self.comics_picture_label[comics_title].setAlignment(Qt.AlignCenter) self.comics_picture_label[comics_title].setObjectName(comics_title) comicParser = COMICParser('library/' + comic) self.comics_pictures[comics_title] = comicParser.read_book() self.comics_current_page[comics_title] = 0 self.comics_picture_label[comics_title].setPixmap(QPixmap( 'library/' + comics_title + '/' + self.comics_pictures[comics_title][0])) close_button = QPushButton('Fermer la BD', self.comics_tabs[comics_title]) close_button.setGeometry(250, 510, 100, 25) def close(): self.remove_tab(self.tab_widget.indexOf(self.comics_tabs[comics_title])) close_button.clicked.connect(close) return read_comic def remove_tab(self, index, tab_type = ''): if index > 0 and tab_type == 'edit': self.tab_widget.removeTab(index) self.comics_tabs_index = {} for key, value in self.comics_tabs.items(): self.comics_tabs_index[key] = self.tab_widget.indexOf(value) elif index > 0: self.tab_widget.removeTab(index) comics_to_close = None for key, value in self.comics_tabs_index.items(): if value == index: comics_to_close = key del self.comics_tabs[comics_to_close] del self.comics_current_page[comics_to_close] del self.comics_picture_label[comics_to_close] del self.comics_pictures[comics_to_close] self.comics_tabs_index = {} for key, value in self.comics_tabs.items(): self.comics_tabs_index[key] = self.tab_widget.indexOf(value) def make_open_edit_tab(self, file): def open_edit_tab(): comicParser = COMICParser('library/' + file) file_infos = comicParser.getMetadata(os.path.splitext(file)[0]) edit_tab = QWidget() edit_tab.setObjectName('edit_tab') self.tab_widget.addTab(edit_tab, 'Modifier ' + file) QLabel('Auteur :', edit_tab).setGeometry(0, 0, 400, 20) author_text = QLineEdit('<unknown>', edit_tab) if 'author' in file_infos: author_text.setText(file_infos['author']) author_text.setGeometry(0, 20, 400, 20) author_text.setObjectName('author_text') QLabel('Tags :', edit_tab).setGeometry(0, 50, 400, 20) tags_text = QLineEdit('tag1, tag2...', edit_tab) if 'tags' in file_infos: tags_text.setText(file_infos['tags']) tags_text.setGeometry(0, 70, 400, 20) tags_text.setObjectName('tags_text') QLabel('Quality :', edit_tab).setGeometry(0, 100, 400, 20) quality_text = QLineEdit('-/10', edit_tab) if 'quality' in file_infos: quality_text.setText(file_infos['quality']) quality_text.setGeometry(0, 120, 400, 20) quality_text.setObjectName('quality_text') cancel_button = QPushButton('Annuler', edit_tab) cancel_button.setGeometry(190, 150, 100, 25) validate_button = QPushButton('Enregistrer', edit_tab) validate_button.setGeometry(300, 150, 100, 25) def cancel(): self.remove_tab(self.tab_widget.indexOf(edit_tab), 'edit') def edit_info(): comicParser.generate_metadata(os.path.splitext(file)[0], author_text.text(), tags_text.text(), quality_text.text()) self.remove_tab(self.tab_widget.indexOf(edit_tab), 'edit') self.set_library() cancel_button.clicked.connect(cancel) validate_button.clicked.connect(edit_info) return open_edit_tab def set_library(self): self.table_widget.clear() self.table_widget.setRowCount(1) self.table_widget.setItem(0, 0, QTableWidgetItem()) self.table_widget.item(0, 0).setText('Cover') self.table_widget.setItem(0, 1, QTableWidgetItem()) self.table_widget.item(0, 1).setText('Titre') self.table_widget.setItem(0, 2, QTableWidgetItem()) self.table_widget.item(0, 2).setText('Auteur') self.table_widget.setItem(0, 3, QTableWidgetItem()) self.table_widget.item(0, 3).setText('Année') self.table_widget.setItem(0, 4, QTableWidgetItem()) self.table_widget.item(0, 4).setText('Tags') self.table_widget.setItem(0, 5, QTableWidgetItem()) self.table_widget.item(0, 5).setText('Quality') self.library_files = get_library_files() index = 0 for comic in self.library_files: if os.path.splitext(comic)[1].lower() == '.cbz': unzip('library/' + comic, 'library/' + os.path.splitext(comic)[0]) else: unrar('library/' + comic, 'library/' + os.path.splitext(comic)[0]) index += 1 self.table_widget.insertRow(index) cover_widget = QWidget() self.table_widget.setCellWidget(index, 0, cover_widget) self.table_widget.setRowHeight(index, 150) comicParser = COMICParser('library/' + comic) pictures = comicParser.read_book() cover_label = QLabel(cover_widget) cover_label.setGeometry( QRect(0, 0, 100, 150)) cover_label.setScaledContents(True) cover_label.setAlignment(Qt.AlignCenter) cover_label.setObjectName(comic) cover_label.setPixmap(QPixmap('library/' + os.path.splitext(comic)[0] + '/' + pictures[0])) self.table_widget.setItem(index, 1, QTableWidgetItem()) self.table_widget.item(index, 1).setText(os.path.splitext(comic)[0]) metadata = comicParser.getMetadata(os.path.splitext(comic)[0]) self.table_widget.setItem(index, 3, QTableWidgetItem()) self.table_widget.item(index, 3).setText(metadata['year']) if 'author' and 'tags' and 'quality' in metadata: self.table_widget.setItem(index, 2, QTableWidgetItem()) self.table_widget.item(index, 2).setText(metadata['author']) self.table_widget.setItem(index, 4, QTableWidgetItem()) self.table_widget.item(index, 4).setText(metadata['tags']) self.table_widget.setItem(index, 5, QTableWidgetItem()) self.table_widget.item(index, 5).setText(metadata['quality']) read_button = QPushButton(self.table_widget) self.table_widget.setCellWidget(index, 6, read_button) read_button.setText('Lire la BD') read_button.clicked.connect(self.make_read_comic(comic)) edit_button = QPushButton(self.table_widget) self.table_widget.setCellWidget(index, 7, edit_button) edit_button.setText('Modifier les infos') edit_button.clicked.connect(self.make_open_edit_tab(comic))
class MainWindow(QMainWindow): def __init__(self, *args, **kwargs): self.homepage = kwargs["homepage"] del kwargs["homepage"] self.zeronet_base_url = kwargs["zeronet_base_url"] del kwargs["zeronet_base_url"] url = "%s/%s/" % (self.zeronet_base_url, self.homepage) if "url" in kwargs: url = kwargs["url"] del kwargs["url"] if "zeronet_path" in kwargs: self.zeronet_path = kwargs["zeronet_path"] del kwargs["zeronet_path"] super(MainWindow, self).__init__(*args, **kwargs) # Tabs self.tabs = QTabWidget() self.tabs.setTabsClosable(True) self.tabs.tabCloseRequested.connect(self.close_tab) # New tab button #self.tab_add_button_index = self.tabs.addTab(QWidget(), '+') self.add_tab_button = QToolButton() self.add_tab_button.setText('+') self.add_tab_button.setStyleSheet( 'QToolButton {border: none; margin: 4px 1ex 4px 0px; height: 480px; border-left: 1px solid lightgrey; padding: 0px 4px 0px 4px; font-weight: bold; color: #5d5b59}' 'QToolButton:hover { background-color: lightgrey }' 'QToolButton:pressed { background-color: grey }') self.add_tab_button.clicked.connect(self.new_tab_clicked) self.tabs.setCornerWidget(self.add_tab_button) # Navigation bar self.navigation = NavigationBar() self.navigation.url_bar.returnPressed.connect(self.navigate_to_url) # Back self.navigation.back_btn.triggered.connect( lambda: self.tabs.currentWidget().back()) # Next self.navigation.next_btn.triggered.connect( lambda: self.tabs.currentWidget().forward()) # Reload self.navigation.reload_btn.triggered.connect( lambda: self.tabs.currentWidget().reload()) self.navigation.shortcut_reload.activated.connect( lambda: self.tabs.currentWidget().reload()) self.navigation.shortcut_reload_f5.activated.connect( lambda: self.tabs.currentWidget().reload()) # Home self.navigation.home_btn.triggered.connect(self.go_home) # Menu: Edit config action self.navigation.edit_config_action.triggered.connect( self.edit_zeronet_config_file) # Add new tab self.add_new_tab(url, "Home") # Get everything fitting in the main window self.addToolBar(self.navigation) self.setCentralWidget(self.tabs) self.show() self.setWindowTitle("ZeroNet Browser") self.setWindowIcon(QIcon("icons/zeronet-logo.svg")) self.showMaximized() def contextMenuEvent(self, event): print(event) def update_url_bar(self, q, browser=None): if browser != self.tabs.currentWidget(): # If this signal is not from the current tab, ignore return url_array = q.toString().split('/')[3:] formatted_url = '/'.join(str(x) for x in url_array) self.navigation.url_bar.setText('zero://' + formatted_url) self.navigation.url_bar.setCursorPosition(0) if (self.tabs.currentWidget().can_go_back()): self.navigation.back_btn.setDisabled(False) else: self.navigation.back_btn.setDisabled(True) if (self.tabs.currentWidget().can_go_forward()): self.navigation.next_btn.setDisabled(False) else: self.navigation.next_btn.setDisabled(True) def navigate_to_url(self): # Get url url = self.navigation.url_bar.text() if url.startswith('zero://'): # ZeroNet protocol url_array = url.split('/') url = self.zeronet_base_url + '/' + url_array[2] elif url.startswith('http://') or url.startswith('https://'): # http protocol pass else: # Nothing mentionned url = self.zeronet_base_url + '/' + url self.tabs.currentWidget().setUrl(QUrl(url)) def go_home(self): self.tabs.currentWidget().setUrl( QUrl("%s/%s/" % (self.zeronet_base_url, self.homepage))) def new_tab_clicked(self): self.add_new_tab("%s/%s/" % (self.zeronet_base_url, self.homepage), "Home") def get_link_url_from_context_menu(self): tab = self.tabs.currentWidget() page = tab.page() context = page.contextMenuData() qurl = context.linkUrl() return qurl.url() def open_in_new_tab(self): url = self.get_link_url_from_context_menu() self.add_new_tab(url, "Home") # Doesnt feel right to have it here but it is working def open_in_new_window(self): url = self.get_link_url_from_context_menu() kwargs = {"url": url} self.window = self.__class__(**kwargs) def add_new_tab(self, qurl, label): # Instead of browser it should be called WebView ! browser = Browser() # Triggered open in new tab openLinkInNewTabAction = browser.pageAction( QWebEnginePage.OpenLinkInNewTab) openLinkInNewWindowAction = browser.pageAction( QWebEnginePage.OpenLinkInNewWindow) openLinkInNewTabAction.triggered.connect(self.open_in_new_tab) openLinkInNewWindowAction.triggered.connect(self.open_in_new_window) self.addAction(openLinkInNewTabAction) browser.urlChanged.connect( lambda qurl, browser=browser: self.update_url_bar(qurl, browser)) indexTab = self.tabs.addTab(browser, label) # Maybe change current index after loading? self.tabs.setCurrentIndex(indexTab) # We need to update the url ! if qurl.startswith('zero://'): # ZeroNet protocol url_array = qurl.split('/') qurl = self.zeronet_base_url + '/' + url_array[2] elif qurl.startswith('http://') or qurl.startswith('https://'): # http protocol pass else: # Nothing mentionned qurl = self.zeronet_base_url + '/' + qurl currentTab = self.tabs.currentWidget() currentTab.loadFinished.connect(self.page_loaded) index = self.tabs.currentIndex() currentTab.titleChanged.connect( lambda title, index=index: self.tabs.setTabText(index, title)) currentTab.iconChanged.connect( lambda icon, index=index: self.tabs.setTabIcon(index, icon)) currentTab.setUrl(QUrl(qurl)) return indexTab def page_loaded(self, ok): if ok: currentTab = self.tabs.currentWidget() index = self.tabs.currentIndex() label = currentTab.title() icon = currentTab.icon() self.tabs.setTabIcon(index, icon) self.tabs.setTabText(index, label) def close_tab(self, index): if self.tabs.count() == 1: self.tabs.currentWidget().setUrl( QUrl("%s/%s/" % (self.zeronet_base_url, self.homepage))) return self.tabs.removeTab(index) def edit_zeronet_config_file(self): filepath = os.path.join(os.sep, self.zeronet_path, "zeronet.conf") if sys.platform.startswith('darwin'): # macOS subprocess.run(['open', filepath]) elif sys.platform.startswith('win'): # Windows os.startfile(filepath) else: # linux variants subprocess.run(['xdg-open', filepath])
class MainWindow(QMainWindow): """[summary] Main window class containing the main window and its associated methods. Args: QMainWindow (QObject): See qt documentation for more info. """ # Send logging parameters to worker method logging_requested = QtCore.pyqtSignal(str, str, bool, str, object) def __init__(self, *args, **kwargs): """[summary] Function to initialise the Main Window, which will hold all the subsequent widgets to be created. """ super(MainWindow, self).__init__(*args, **kwargs) self._tdc1_dev = None # tdc1 device object self._dev_mode = '' # 0 = 'singles', 1 = 'pairs', 3 = 'timestamp' self.device_path = '' # Device path, eg. 'COM4' self.integration_time = 1 self._logfile_name = '' # Track the logfile(csv) being used by GUI self._ch_start = 1 # Start channel for pairs self._ch_stop = 3 # Stop channel for pairs self.plotSamples = 501 # Number of data points to plot self.log_flag = False # Flag to track if data is being logged to csv file self.acq_flag = False # Track if data is being acquired self._radio_flags = [ 0, 0, 0, 0 ] # Tracking which radio buttons are selected. All 0s by default self.logger = None # Variable that will hold the logWorker object self.logger_thread = None # Variable that will hold the QThread object self.initUI() # UI is initialised afer the class variables are defined self._plot_tab = self.tabs.currentIndex( ) # Counts graph = 0, Coincidences graph = 1 self.idx = min(len(self.y1), self.plotSamples) # Index for plotting def initUI(self): """[summary] Contains all the UI elements and associated functionalities. """ defaultFont = QtGui.QFont("Helvetica", 14) #---------Buttons---------# self.scanForDevice_Button = QtWidgets.QPushButton( "Scan for Device", self) self.scanForDevice_Button.clicked.connect(self.updateDevList) #self.scanForDevice_Button.setFixedSize(QSize(115, 35)) self.liveStart_Button = QtWidgets.QPushButton("Live Start", self) self.liveStart_Button.clicked.connect(self.liveStart) #self.liveStart_Button.setFixedSize(QSize(115, 35)) self.selectLogfile_Button = QtWidgets.QPushButton( "Select Logfile", self) self.selectLogfile_Button.clicked.connect(self.selectLogfile) #self.selectLogfile_Button.setFixedSize(QSize(115, 35)) # setAutoExclusive method is used to toggle the radio buttons independently. self.radio1_Button = QRadioButton("Channel 1", self) self.radio1_Button.setStyleSheet('color: red; font-size: 14px') self.radio1_Button.setAutoExclusive(False) self.radio1_Button.toggled.connect( lambda: self.displayPlot1(self.radio1_Button)) self.radio2_Button = QRadioButton("Channel 2", self) self.radio2_Button.setStyleSheet('color: green; font-size: 14px') self.radio2_Button.setAutoExclusive(False) self.radio2_Button.toggled.connect( lambda: self.displayPlot2(self.radio2_Button)) self.radio3_Button = QRadioButton("Channel 3", self) self.radio3_Button.setStyleSheet('color: blue; font-size: 14px') self.radio3_Button.setAutoExclusive(False) self.radio3_Button.toggled.connect( lambda: self.displayPlot3(self.radio3_Button)) self.radio4_Button = QRadioButton("Channel 4", self) self.radio4_Button.setStyleSheet('color: black; font-size: 14px') self.radio4_Button.setAutoExclusive(False) self.radio4_Button.toggled.connect( lambda: self.displayPlot4(self.radio4_Button)) #---------Buttons---------# #---------Labels---------# #labelFontSize = "font-size: 18px" self.deviceLabel = QtWidgets.QLabel("Device:", self) self.deviceModeLabel = QtWidgets.QLabel("GUI Mode:", self) self.logfileLabel = QtWidgets.QLabel('', self) self.samplesLabel = QtWidgets.QLabel('Plot Samples:', self) self.integrationLabel = QtWidgets.QLabel("Integration time (ms):", self) self.Ch1CountsLabel = QtWidgets.QLabel("0", self) self.Ch1CountsLabel.setStyleSheet("color: red; font-size: 128px") self.Ch1CountsLabel.setAlignment(QtCore.Qt.AlignCenter) self.Ch2CountsLabel = QtWidgets.QLabel("0", self) self.Ch2CountsLabel.setStyleSheet("color: green; font-size: 128px") self.Ch2CountsLabel.setAlignment(QtCore.Qt.AlignCenter) self.Ch3CountsLabel = QtWidgets.QLabel("0", self) self.Ch3CountsLabel.setStyleSheet("color: blue; font-size: 128px") self.Ch3CountsLabel.setAlignment(QtCore.Qt.AlignCenter) self.Ch4CountsLabel = QtWidgets.QLabel("0", self) self.Ch4CountsLabel.setStyleSheet("color: black; font-size: 128px") self.Ch4CountsLabel.setAlignment(QtCore.Qt.AlignCenter) self.startChannelLabel = QtWidgets.QLabel("Start Channel:", self) self.stopChannelLabel = QtWidgets.QLabel("Stop Channel:", self) self.centerLabel = QtWidgets.QLabel("Center:", self) self.pairsRateLabel = QtWidgets.QLabel("Pairs/sec: <br>" + "0") self.pairsRateLabel.setStyleSheet("font-size: 64px") self.pairsRateLabel.setAlignment(QtCore.Qt.AlignCenter) self.resolutionTextLabel = QtWidgets.QLabel("Bin Width:", self) #---------Labels---------# #---------Interactive Fields---------# self.integrationSpinBox = QSpinBox(self) self.integrationSpinBox.setRange( 0, 65535) # Max integration time based on tdc1 specs self.integrationSpinBox.setValue(1000) # Default 1000ms = 1s self.integrationSpinBox.setKeyboardTracking( False ) # Makes sure valueChanged signal only fires when you want it to self.integrationSpinBox.valueChanged.connect(self.update_intTime) dev_list = serial_connection.search_for_serial_devices( tdc1.TimeStampTDC1.DEVICE_IDENTIFIER) self.devCombobox = QComboBox(self) self.devCombobox.addItem('Select your device') self.devCombobox.addItems(dev_list) self.devCombobox.currentTextChanged.connect(self.selectDevice) _dev_modes = ['singles', 'pairs'] self.modesCombobox = QComboBox(self) self.modesCombobox.addItem('Select mode') self.modesCombobox.addItems(_dev_modes) self.modesCombobox.currentTextChanged.connect(self.selectDeviceMode) _channels = ['1', '2', '3', '4'] self.channelsCombobox1 = QComboBox(self) self.channelsCombobox1.addItem('Select') self.channelsCombobox1.addItems(_channels) self.channelsCombobox1.setCurrentIndex(1) self.channelsCombobox1.currentTextChanged.connect(self.updateStart) self.channelsCombobox2 = QComboBox(self) self.channelsCombobox2.addItem('Select') self.channelsCombobox2.addItems(_channels) self.channelsCombobox2.setCurrentIndex(3) self.channelsCombobox2.currentTextChanged.connect(self.updateStop) self.samplesSpinbox = QSpinBox(self) self.samplesSpinbox.setRange(0, 501) self.samplesSpinbox.setValue(501) # Default plot 501 data points self.samplesSpinbox.setKeyboardTracking(False) self.samplesSpinbox.valueChanged.connect(self.updatePlotSamples) self.centerSpinbox = QSpinBox(self) self.centerSpinbox.setRange(0, 1000) self.centerSpinbox.setKeyboardTracking(False) self.resolutionSpinbox = QSpinBox(self) self.resolutionSpinbox.setRange(0, 1000) self.resolutionSpinbox.setKeyboardTracking(False) self.resolutionSpinbox.valueChanged.connect(self.updateBins) #---------Interactive Fields---------# #---------PLOTS---------# # Initiating plot data variables # Plot 1 - Four channel counts plot self.x = [] self.y1 = [] self.y2 = [] self.y3 = [] self.y4 = [] self.xnew = [] self.y1new = [] self.y2new = [] self.y3new = [] self.y4new = [] self.y_data = [self.y1new, self.y2new, self.y3new, self.y4new] # Plot 2 - Time difference histogram (Channel cross-correlation) self.bins = 501 self.binsize = 2 # milliseconds self.x0 = np.arange(0, self.bins * self.binsize, self.bins) self.y0 = np.zeros_like(self.x0) self.x0new = [] self.y0new = [] font = QtGui.QFont("Arial", 24) labelStyle = '<span style=\"color:black;font-size:25px\">' # Setting up plot window 1 (Plot Widget) self.tdcPlot = pg.PlotWidget(title="Counts Graph") self.tdcPlot.setBackground('w') self.tdcPlot.setLabel('left', labelStyle + 'Counts') self.tdcPlot.setLabel('bottom', labelStyle + 'Sample Number') self.tdcPlot.getAxis('left').tickFont = font self.tdcPlot.getAxis('bottom').tickFont = font self.tdcPlot.getAxis('bottom').setPen(color='k') self.tdcPlot.getAxis('left').setPen(color='k') self.tdcPlot.showGrid(y=True) # Setting up plot window 2 (Plot Widget) self.tdcPlot2 = pg.PlotWidget(title="Coincidences Histogram") self.tdcPlot2.setBackground('w') self.tdcPlot2.setLabel('left', labelStyle + 'Coincidences') self.tdcPlot2.setLabel('bottom', labelStyle + 'Time Delay') self.tdcPlot2.getAxis('left').tickFont = font self.tdcPlot2.getAxis('bottom').tickFont = font self.tdcPlot2.getAxis('bottom').setPen(color='k') self.tdcPlot2.getAxis('left').setPen(color='k') self.tdcPlot2.showGrid(y=True) # Setting up data plots (Plot data item) self.lineStyle1 = pg.mkPen(width=2, color='r') # Red self.lineStyle2 = pg.mkPen(width=2, color='g') # Green self.lineStyle3 = pg.mkPen(width=2, color='b') # Blue self.lineStyle4 = pg.mkPen(width=2, color='k') # Black self.lineStyle0 = pg.mkPen(width=1, color='r') # Plotting the graph - https://pyqtgraph.readthedocs.io/en/latest/plotting.html for organisation of plotting classes # Take note: multiple plotDataItems can sit on one plotWidget self.linePlot1 = self.tdcPlot.plot(self.x, self.y1, pen=self.lineStyle1) self.linePlot2 = self.tdcPlot.plot(self.x, self.y2, pen=self.lineStyle2) self.linePlot3 = self.tdcPlot.plot(self.x, self.y3, pen=self.lineStyle3) self.linePlot4 = self.tdcPlot.plot(self.x, self.y4, pen=self.lineStyle4) self.histogramPlot = self.tdcPlot2.plot(self.x0, self.y0, pen=self.lineStyle0, symbol='x', symbolPen='b', symbolBrush=0.2) self.linePlots = [ self.linePlot1, self.linePlot2, self.linePlot3, self.linePlot4 ] #---------PLOTS---------# #---------Main Window---------# self.setWindowTitle("TDC-1") #---------Main Window---------# #---------Tabs---------# self.tabs = QTabWidget() self.tab1 = QWidget() self.layout = QGridLayout() self.layout.addWidget(self.tdcPlot, 0, 0, 5, 5) self.layout.addWidget(self.Ch1CountsLabel, 0, 5) self.layout.addWidget(self.Ch2CountsLabel, 1, 5) self.layout.addWidget(self.Ch3CountsLabel, 2, 5) self.layout.addWidget(self.Ch4CountsLabel, 3, 5) self.tab1.setLayout(self.layout) self.tabs.addTab(self.tab1, "Counts") self.tab2 = QWidget() self.layout2 = QGridLayout() self.layout2.addWidget(self.tdcPlot2, 0, 0, 5, 5) self.layout2.addWidget(self.pairsRateLabel, 0, 5) self.tab2.setLayout(self.layout2) self.tabs.addTab(self.tab2, "Coincidences") self.tabs.currentChanged.connect(self.update_plot_tab) #---------Tabs---------# #Layout self.grid = QGridLayout() self.grid.setSpacing(20) self.grid.addWidget(self.deviceLabel, 0, 0) self.grid.addWidget(self.devCombobox, 0, 1) self.grid.addWidget(self.deviceModeLabel, 0, 2) self.grid.addWidget(self.modesCombobox, 0, 3, 1, 1) self.grid.addWidget(self.integrationLabel, 1, 0) self.grid.addWidget(self.integrationSpinBox, 1, 1) self.grid.addWidget(self.samplesLabel, 1, 2) self.grid.addWidget(self.samplesSpinbox, 1, 3, 1, 1) self.grid.addWidget(self.liveStart_Button, 2, 0) self.grid.addWidget(self.scanForDevice_Button, 2, 1) self.grid.addWidget(self.selectLogfile_Button, 2, 2) self.grid.addWidget(self.logfileLabel, 2, 3) self.grid.addWidget(self.tabs, 4, 0, 5, 4) self.singlesGroupbox = QGroupBox('Singles') self.singlesLayout = QHBoxLayout() self.singlesLayout.addWidget(self.radio1_Button) self.singlesLayout.addWidget(self.radio2_Button) self.singlesLayout.addWidget(self.radio3_Button) self.singlesLayout.addWidget(self.radio4_Button) self.singlesGroupbox.setLayout(self.singlesLayout) self.grid.addWidget(self.singlesGroupbox, 3, 0, 1, 2) self.pairsGroupbox = QGroupBox('Pairs') self.pairsSpinLayout = QHBoxLayout() self.pairsSpinLayout.addWidget(self.startChannelLabel) self.pairsSpinLayout.addWidget(self.channelsCombobox1) self.pairsSpinLayout.addWidget(self.stopChannelLabel) self.pairsSpinLayout.addWidget(self.channelsCombobox2) #self.pairsLabelLayout = QHBoxLayout() self.pairsCenterLayout = QHBoxLayout() self.pairsCenterLayout.addWidget(self.centerLabel) self.pairsCenterLayout.addWidget(self.centerSpinbox) self.pairsCenterLayout.addWidget(self.resolutionTextLabel) self.pairsCenterLayout.addWidget(self.resolutionSpinbox) self.pairsLayout = QVBoxLayout() #self.pairsLayout.addLayout(self.pairsLabelLayout) self.pairsLayout.addLayout(self.pairsSpinLayout) self.pairsLayout.addLayout(self.pairsCenterLayout) self.pairsGroupbox.setLayout(self.pairsLayout) self.grid.addWidget(self.pairsGroupbox, 3, 2, 1, 2) #Main Widget (on which the grid is to be implanted) self.mainwidget = QWidget() self.mainwidget.layout = self.grid self.mainwidget.setLayout(self.mainwidget.layout) self.mainwidget.setFont(defaultFont) self.setCentralWidget(self.mainwidget) # Connected to devComboBox.currentTextChanged @QtCore.pyqtSlot(str) def selectDevice(self, devPath: str): # Only allow resetting + changing device if not currently collecting data # Add msg box to allow user confirmation if self.acq_flag == False: self.StrongResetInternalVariables() self.resetDataAndPlots() self.resetGUIelements() if devPath != 'Select your device': self._tdc1_dev = tdc1.TimeStampTDC1(devPath) self.device_path = devPath @QtCore.pyqtSlot() def updateDevList(self): self.devCombobox.clear() self.devCombobox.addItem('Select your device') devices = serial_connection.search_for_serial_devices( tdc1.TimeStampTDC1.DEVICE_IDENTIFIER) self.devCombobox.addItems(devices) # Connected to modesCombobox.currentTextChanged @QtCore.pyqtSlot(str) def selectDeviceMode(self, newMode: str): # Only allow resetting + device mode change if not currently collecting data # Add msg box to allow user confirmation if self.acq_flag == False: self.WeakResetInternalVariables() self.resetDataAndPlots() self.resetGUIelements() if newMode != 'Select mode': if self._tdc1_dev == None: self._tdc1_dev = tdc1.TimeStampTDC1(self.device_path) self._tdc1_dev.mode = newMode # Setting tdc1 mode with @setter self._dev_mode = newMode # Update plot index on plot tab change @QtCore.pyqtSlot() def update_plot_tab(self): self._plot_tab = self.tabs.currentIndex() # Update integration time on spinbox value change @QtCore.pyqtSlot(int) def update_intTime(self, int_time: int): # Convert to seconds self.integration_time = int_time * 1e-3 if self.logger: self.logger.int_time = int_time * 1e-3 @QtCore.pyqtSlot(int) def updatePlotSamples(self, samples: int): self.plotSamples = samples # Click Live Start button to get started! @QtCore.pyqtSlot() # Connected to self.liveStart_button.clicked def liveStart(self): #If currently live plotting if self.acq_flag is True and self.liveStart_Button.text( ) == "Live Stop": self.acq_flag = False self.logger.active_flag = False # To stop logger from looping self.selectLogfile_Button.setEnabled(True) self.liveStart_Button.setText("Live Start") time.sleep(1) # Pause to let all loops end self._tdc1_dev = None # Destroy tdc1_dev object self.logger = None # Destroy logger self.logger_thread = None # and thread...? #If not currently live plotting elif self.acq_flag is False and self.liveStart_Button.text( ) == "Live Start": if self._tdc1_dev == None: self._tdc1_dev = tdc1.TimeStampTDC1( self.devCombobox.currentText()) self.acq_flag = True self.resetDataAndPlots() self.selectLogfile_Button.setEnabled(False) self.modesCombobox.setEnabled(True) self.liveStart_Button.setText("Live Stop") self.startLogging() # Logging def startLogging(self): """[summary] Creation process of worker object and QThread. """ # Create worker instance and a thread self.logger = logWorker() self.logger_thread = QtCore.QThread( self) # QThread is not a thread, but a thread MANAGER # Assign worker to the thread and start the thread self.logger.moveToThread(self.logger_thread) self.logger_thread.start( ) # This is where the thread is actually created, I think # Connect signals and slots AFTER moving the object to the thread self.logging_requested.connect(self.logger.log_which_data) self.logger.data_is_logged.connect(self.update_data_from_thread) self.logger.histogram_logged.connect(self.updateHistogram) self.logger.int_time = int( self.integrationSpinBox.text()) * 1e-3 # Convert to seconds #self.log_flag = True self.logging_requested.emit(self._logfile_name, self.device_path, self.log_flag, self._dev_mode, \ self._tdc1_dev) @QtCore.pyqtSlot() def selectLogfile(self): if self.acq_flag == False: if self.selectLogfile_Button.text() == 'Select Logfile': default_filetype = 'csv' start = datetime.now().strftime( "%Y%m%d_%Hh%Mm%Ss ") + "_TDC1." + default_filetype self._logfile_name = QtGui.QFileDialog.getSaveFileName( self, "Save to log file", start)[0] self.logfileLabel.setText(self._logfile_name) if self._logfile_name != '': #self.startLogging_Button.setEnabled(True) self.log_flag = True self.selectLogfile_Button.setText('Unselect Logfile') elif self.selectLogfile_Button.text() == 'Unselect Logfile': self.logfileLabel.setText('') self.selectLogfile_Button.setText('Select Logfile') # Updating data # Connected to data_is_logged signal @QtCore.pyqtSlot(tuple, str, list) def update_data_from_thread(self, data: tuple, dev_mode: str, radio_flags: list): if len(self.x) == PLT_SAMPLES: self.x = self.x[1:] self.x.append(self.x[-1] + 1) self.y1 = self.y1[1:] self.y2 = self.y2[1:] self.y3 = self.y3[1:] self.y4 = self.y4[1:] else: self.x.append(len(self.x) + 1) # Takes care of empty list case as well self.y1.append(data[0]) self.y2.append(data[1]) self.y3.append(data[2]) self.y4.append(data[3]) self.idx = min(len(self.y1), self.plotSamples) self.y1new = self.y1[-self.idx:] self.y2new = self.y2[-self.idx:] self.y3new = self.y3[-self.idx:] self.y4new = self.y4[-self.idx:] self.y_data = [self.y1new, self.y2new, self.y3new, self.y4new] self._radio_flags = radio_flags self.Ch1CountsLabel.setText(str(data[0])) self.Ch2CountsLabel.setText(str(data[1])) self.Ch3CountsLabel.setText(str(data[2])) self.Ch4CountsLabel.setText(str(data[3])) self.updatePlots(self._radio_flags) # Updating plots 1-4 def updatePlots(self, radio_flags: list): for i in range(len(radio_flags)): if radio_flags[i] == 1: self.linePlots[i].setData(self.x[-self.idx:], self.y_data[i][-self.idx:]) # Radio button slots (functions) @QtCore.pyqtSlot('PyQt_PyObject') def displayPlot1(self, b: QRadioButton): if self.acq_flag == True: if b.isChecked() == True: # Possible to clear self.x and self.y1 without disrupting the worker loop? self.updatePlots(self._radio_flags) self.linePlot1.setPen(self.lineStyle1) self.logger.radio_flags[0] = 1 self._radio_flags[0] = 1 elif b.isChecked() == False: self.linePlot1.setPen(None) self.logger.radio_flags[0] = 0 self._radio_flags[0] = 0 @QtCore.pyqtSlot('PyQt_PyObject') def displayPlot2(self, b: QRadioButton): if self.acq_flag == True: if b.isChecked() == True: self.updatePlots(self._radio_flags) self.linePlot2.setPen(self.lineStyle2) self.logger.radio_flags[1] = 1 self._radio_flags[1] = 1 elif b.isChecked() == False: self.linePlot2.setPen(None) self.logger.radio_flags[1] = 0 self._radio_flags[1] = 0 @QtCore.pyqtSlot('PyQt_PyObject') def displayPlot3(self, b: QRadioButton): if self.acq_flag == True: if b.isChecked() == True: self.updatePlots(self._radio_flags) self.linePlot3.setPen(self.lineStyle3) self.logger.radio_flags[2] = 1 self._radio_flags[2] = 1 elif b.isChecked() == False: self.linePlot3.setPen(None) self.logger.radio_flags[2] = 0 self._radio_flags[2] = 0 @QtCore.pyqtSlot('PyQt_PyObject') def displayPlot4(self, b: QRadioButton): if self.acq_flag == True: if b.isChecked(): self.updatePlots(self._radio_flags) self.linePlot4.setPen(self.lineStyle4) self.logger.radio_flags[3] = 1 self._radio_flags[3] = 1 elif b.isChecked() == False: self.linePlot4.setPen(None) self.logger.radio_flags[3] = 0 self._radio_flags[3] = 0 @QtCore.pyqtSlot(str) def updateStart(self, channel: str): cs = int(channel) if self.acq_flag == True and self.modesCombobox.currentText( ) == "pairs": self._ch_start = cs if self.logger: self.logger.ch_start = cs @QtCore.pyqtSlot(str) def updateStop(self, channel: str): cs = int(channel) if self.acq_flag == True and self.modesCombobox.currentText( ) == "pairs": self._ch_stop = cs if self.logger: self.logger.ch_stop = cs # Histogram # Connected to histogram_logged signal def updateHistogram(self, g2_data: dict): # {int - ch_start counts, int- ch_stop counts, int - actual acq time, float - time bins, float - histogram values} # time bins and histogram vals are both np arrays incremental_y = g2_data['histogram'] incremental_y_int = incremental_y.astype(np.int32) self.y0 += incremental_y_int center = self.centerSpinbox.currentValue() idx = int(-(-center // self.binsize)) # Upside-down floor div aka ceiling div startidx = idx - idx_width stopidx = idx + idx_width try: self.x0new = self.x0[startidx:stopidx] self.y0new = self.y0[startidx:stopidx] # If startidx is negative except IndexError: try: self.x0new = self.x0[:stopidx] self.y0new = self.y0[:stopidx] except IndexError: try: self.x0new = self.x0[startidx:] self.y0new = self.y0[startidx:] except: pass self.histogramPlot.setData(self.x0new, self.y0new) totalpairs = np.sum(self.y0, dtype=np.int32) self.pairsRateLabel.setText("<font size=24>Pairs/sec: </font><br>" + "<font size=96>" + totalpairs + "</font>") # Reserve in case above func fails # def updateHistogram(self, g2_data: dict): # # {int - ch_start counts, int- ch_stop counts, int - actual acq time, float - time bins, float - histogram values} # # time bins and histogram vals are both np arrays # incremental_y = g2_data['histogram'] # incremental_y_int = incremental_y.astype(np.int32) # self.y0 += incremental_y_int # self.idx = min(len(self.y0), self.plotSamples) # self.x0new = self.x0[:(self.idx)] # self.y0new = self.y0[:(self.idx)] # self.histogramPlot.setData(self.x0new, self.y0new) # totalpairs = np.sum(self.y0, dtype=np.int32) # self.pairsRateLabel.setText("<font size=24>Pairs/sec: </font><br>" + "<font size=96>" + totalpairs + "</font>") @QtCore.pyqtSlot(int) def updateBins(self, bin_width): self.binsize = bin_width self.x0 = np.arange(0, self.bins * self.binsize, self.binsize) # For future use def StrongResetInternalVariables(self): self.integration_time = 1 self._logfile_name = '' # Track the logfile(csv) being used by GUI self.resetWorkerAndThread() self._tdc1_dev = None # tdc1 device object self._dev_mode = '' # 0 = 'singles', 1 = 'pairs', 3 = 'timestamp' self.device_path = '' # Device path, eg. 'COM4' def WeakResetInternalVariables(self): # Excludes resetting variables relating to the device object (device, mode, path) self.integration_time = 1 self._logfile_name = '' # Track the logfile(csv) being used by GUI self.resetWorkerAndThread() def resetWorkerAndThread(self): time.sleep(2) # To allow threads to end self.logger = None self.logger_thread = None # For future use def resetGUIelements(self): self.liveStart_Button.setEnabled(True) self.selectLogfile_Button.setEnabled(True) self.logfileLabel.setText('') self.radio1_Button.setChecked(False) self.radio2_Button.setChecked(False) self.radio3_Button.setChecked(False) self.radio4_Button.setChecked(False) self.integrationSpinBox.setValue(1000) self.samplesSpinbox.setValue(501) def resetDataAndPlots(self): self.x0 = np.arange(0, self.bins * self.binsize, self.binsize) self.y0 = np.zeros_like(self.x0) self.x0new = [] self.y0new = [] self.x = [] self.y1 = [] self.y2 = [] self.y3 = [] self.y4 = [] self.xnew = [] self.y1new = [] self.y2new = [] self.y3new = [] self.y4new = [] self.linePlot1.setData(self.x, self.y1) self.linePlot2.setData(self.x, self.y2) self.linePlot3.setData(self.x, self.y3) self.linePlot4.setData(self.x, self.y4) self.histogramPlot.setData(self.x0, self.y0) self.radio1_Button.setChecked(False) self.radio2_Button.setChecked(False) self.radio3_Button.setChecked(False) self.radio4_Button.setChecked(False) self._radio_flags = [0, 0, 0, 0]
class mainwindow(QWidget): def __init__(self, username, dataoptions, driver, semesters): self.username = username self.dataoptions = dataoptions self.driver = driver self.semesters = semesters self.subThread = submitThread(self) self.subThread.finished.connect(self.completed) super().__init__() self.initUI() def initUI(self): self.center() #add the data type label and C C C combobox self.datatypelabel = QLabel(self) self.datatypelabel.setText("Data Pull Type") self.datatypelabel.setAlignment(Qt.AlignCenter) self.datacombo = QComboBox(self) #Sorted by alphabet self.datacombo.addItems(sorted(self.dataoptions.keys())) self.datacombo.currentTextChanged.connect(self.combochange) #add the filter label self.filterlabel = QLabel(self) self.filterlabel.setText('Filters') self.filterlabel.setAlignment(Qt.AlignCenter) #add all of the other filter things self.usernamelabel = QLabel(self) self.usernamelabel.setText("Created By: ") self.usernamecombo = QComboBox(self) self.assignedlabel = QLabel(self) self.assignedlabel.setText("Assigned To: ") self.assignedcombo = QComboBox(self) self.locationlabel = QLabel(self) self.locationlabel.setText("Location: ") self.locationcombo = QComboBox(self) self.categorylabel = QLabel(self) self.categorylabel.setText("Category: ") self.categorycombo = QComboBox(self) self.statuslabels = QLabel(self) self.statuslabels.setText("Status: ") self.statuscombo = QComboBox(self) #add the startdate and end date calendars self.startcal = QCalendarWidget(self) self.startcal.setSelectedDate(date.today() - timedelta(days=30)) self.startcal.setVerticalHeaderFormat(QCalendarWidget.NoVerticalHeader) self.startcal.setGridVisible(True) self.startcal.clicked.connect(self.startdatechange) self.startlabel = QLabel(self) self.startlabel.setText("Start Date: " + self.startcal.selectedDate().toString()) self.startdroplabel = QLabel(self) self.startdroplabel.setText('Autoselect start of : ') self.startdroplabel.setObjectName('desctext') self.startcombo = QComboBox(self) self.startcombo.addItems(self.semesters.keys()) self.startcombo.currentTextChanged.connect(self.startcomboselect) self.endcal = QCalendarWidget(self) self.endcal.setSelectedDate(date.today()) self.endcal.setVerticalHeaderFormat(QCalendarWidget.NoVerticalHeader) self.endcal.setGridVisible(True) self.endcal.clicked.connect(self.enddatechange) self.endlabel = QLabel(self) self.endlabel.setText("End Date: " + self.endcal.selectedDate().toString()) self.enddroplabel = QLabel(self) self.enddroplabel.setText('Autoselect end of : ') self.enddroplabel.setObjectName('desctext') self.endcombo = QComboBox(self) self.endcombo.addItems(self.semesters.keys()) self.endcombo.currentTextChanged.connect(self.endcomboselect) #create the maxreturns things self.maxlabel = QLabel(self) self.maxlabel.setText("Max Returns: ") self.maxlabel.hide() self.maxbox = QLineEdit(self) self.maxbox.setText('10000000') self.maxbox.hide() #add close button self.closebutton = QPushButton('Close', self) self.closebutton.clicked.connect(self.close) #add submit button self.submitbutton = QPushButton('Submit', self) self.submitbutton.clicked.connect(self.subThread.start) self.tabs = QTabWidget() #everything for the data pull tab self.datapulltab = QWidget() datatypelabhbox = QHBoxLayout() datatypelabhbox.addWidget(self.datatypelabel) datatypehbox = QHBoxLayout() datatypehbox.addWidget(self.datacombo) filternamehbox = QHBoxLayout() filternamehbox.addWidget(self.filterlabel) usernamehbox = QHBoxLayout() usernamehbox.addWidget(self.usernamelabel) assignedhbox = QHBoxLayout() assignedhbox.addWidget(self.assignedlabel) locationhbox = QHBoxLayout() locationhbox.addWidget(self.locationlabel) categoryhbox = QHBoxLayout() categoryhbox.addWidget(self.categorylabel) statushbox = QHBoxLayout() statushbox.addWidget(self.statuslabels) dataselectlayout = QVBoxLayout() dataselectlayout.addLayout(datatypelabhbox) dataselectlayout.addLayout(datatypehbox) verticalSpacer = QSpacerItem(20, 40, QSizePolicy.Minimum, QSizePolicy.Expanding) dataselectlayout.addSpacerItem(verticalSpacer) dataselectlayout.addLayout(filternamehbox) dataselectlayout.addLayout(usernamehbox) dataselectlayout.addWidget(self.usernamecombo) dataselectlayout.addLayout(assignedhbox) dataselectlayout.addWidget(self.assignedcombo) dataselectlayout.addLayout(locationhbox) dataselectlayout.addWidget(self.locationcombo) dataselectlayout.addLayout(categoryhbox) dataselectlayout.addWidget(self.categorycombo) dataselectlayout.addLayout(statushbox) dataselectlayout.addWidget(self.statuscombo) dataselectlayout.setSpacing(3) dataselectlayout.addStretch(1) startdrophlayout = QHBoxLayout() startdrophlayout.addWidget(self.startdroplabel) startdrophlayout.addWidget(self.startcombo) startdrophlayout.setSpacing(0) startdrophlayout.addStretch(0) enddropylayout = QHBoxLayout() enddropylayout.addWidget(self.enddroplabel) enddropylayout.addWidget(self.endcombo) enddropylayout.setSpacing(0) enddropylayout.addStretch(0) calendarlayout = QVBoxLayout() calendarlayout.addWidget(self.startlabel) calendarlayout.addLayout(startdrophlayout) calendarlayout.addWidget(self.startcal) calendarlayout.addSpacing(10) calendarlayout.addWidget(self.endlabel) calendarlayout.addLayout(enddropylayout) calendarlayout.addWidget(self.endcal) calendarlayout.setSpacing(3) calendarlayout.addStretch(1) datapullhlayout = QHBoxLayout() datapullhlayout.addLayout(dataselectlayout) datapullhlayout.addSpacing(10) datapullhlayout.addLayout(calendarlayout) datapullvlayout = QVBoxLayout() datapullvlayout.addSpacing(15) datapullvlayout.addLayout(datapullhlayout) self.datapulltab.setLayout(datapullvlayout) #Report things? self.reporttab = QWidget() self.startrepcal = QCalendarWidget(self) self.startrepcal.setSelectedDate(date.today() - timedelta(days=30)) self.startrepcal.setVerticalHeaderFormat( QCalendarWidget.NoVerticalHeader) self.startrepcal.setGridVisible(True) self.startrepcal.clicked.connect(self.startrepdatechange) self.startreplabel = QLabel(self) self.startreplabel.setText("Start Date: " + self.startrepcal.selectedDate().toString()) self.endrepcal = QCalendarWidget(self) self.endrepcal.setSelectedDate(date.today()) self.endrepcal.setVerticalHeaderFormat( QCalendarWidget.NoVerticalHeader) self.endrepcal.setGridVisible(True) self.endrepcal.clicked.connect(self.endrepdatechange) self.endreplabel = QLabel(self) self.endreplabel.setText("End Date: " + self.endrepcal.selectedDate().toString()) self.reporttypelabel = QLabel(self) self.reporttypelabel.setText('Report Type') self.reporttypelabel.setAlignment(Qt.AlignCenter) self.reportdrop = QComboBox(self) self.reportdrop.addItems([x.name for x in report.report_list]) self.reportdrop.currentTextChanged.connect(self.reportcombochange) self.reportactivelabel = QLabel(self) self.reportactivelabel.setText("Active") self.reportactive = QLabel(self) self.reportactive.setText("") self.reportactive.setObjectName('desctext') self.reportauthorlabel = QLabel(self) self.reportauthorlabel.setText("Author") self.reportauthor = QLabel(self) self.reportauthor.setText("") self.reportauthor.setObjectName('desctext') self.reportdesclabel = QLabel(self) self.reportdesclabel.setText("Report Description") self.descbox = QLabel(self) self.descbox.setText("") self.descbox.setWordWrap(True) self.descbox.setFixedWidth(self.usernamecombo.frameGeometry().width() + 53) self.descbox.setObjectName('desctext') self.startrepdroplabel = QLabel(self) self.startrepdroplabel.setObjectName('desctext') self.startrepdroplabel.setText('Autoselect start of : ') self.startrepcombo = QComboBox(self) self.startrepcombo.addItems(self.semesters.keys()) self.startrepcombo.currentTextChanged.connect(self.startrepcomboselect) self.enddropreplabel = QLabel(self) self.enddropreplabel.setText('Autoselect end of : ') self.enddropreplabel.setObjectName('desctext') self.endrepcombo = QComboBox(self) self.endrepcombo.addItems(self.semesters.keys()) self.endrepcombo.currentTextChanged.connect(self.endrepcomboselect) newreportlayout = QVBoxLayout() newreportlayout.addWidget(self.reporttypelabel) newreportlayout.addWidget(self.reportdrop) verticalSpacernew = QSpacerItem(10, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) newreportlayout.addSpacerItem(verticalSpacernew) newreportlayout.addWidget(self.reportauthorlabel) newreportlayout.addWidget(self.reportauthor) verticalSpacernewest = QSpacerItem(10, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) newreportlayout.addSpacerItem(verticalSpacernewest) newreportlayout.addWidget(self.reportactivelabel) newreportlayout.addWidget(self.reportactive) verticalSpacernewer = QSpacerItem(10, 20, QSizePolicy.Minimum, QSizePolicy.Expanding) newreportlayout.addSpacerItem(verticalSpacernewer) newreportlayout.addWidget(self.reportdesclabel) newreportlayout.addWidget(self.descbox) newreportlayout.setSpacing(3) newreportlayout.addStretch(1) startrepdrophlayout = QHBoxLayout() startrepdrophlayout.addWidget(self.startrepdroplabel) startrepdrophlayout.addWidget(self.startrepcombo) startrepdrophlayout.setSpacing(0) startrepdrophlayout.addStretch(0) endrepdropylayout = QHBoxLayout() endrepdropylayout.addWidget(self.enddropreplabel) endrepdropylayout.addWidget(self.endrepcombo) endrepdropylayout.setSpacing(0) endrepdropylayout.addStretch(0) repcallayout = QVBoxLayout() repcallayout.addWidget(self.startreplabel) repcallayout.addLayout(startrepdrophlayout) repcallayout.addWidget(self.startrepcal) repcallayout.addSpacing(10) repcallayout.addWidget(self.endreplabel) repcallayout.addLayout(endrepdropylayout) repcallayout.addWidget(self.endrepcal) repcallayout.setSpacing(3) repcallayout.addStretch(1) reportouterlayout = QHBoxLayout() reportouterlayout.addLayout(newreportlayout) reportouterlayout.addSpacing(10) reportouterlayout.addLayout(repcallayout) reportouterlayoutout = QVBoxLayout() reportouterlayoutout.addSpacing(15) reportouterlayoutout.addLayout(reportouterlayout) self.reporttab.setLayout(reportouterlayoutout) self.tabs.addTab(self.datapulltab, "Data Pull") self.tabs.addTab(self.reporttab, "Reporting") buttonlayout = QHBoxLayout() buttonlayout.addWidget(self.closebutton) buttonlayout.addWidget(self.submitbutton) self.statuslabel = QLabel(self) self.statuslabel.setText("Ready") self.statuslabel.setObjectName('statuslabel') self.statuslabel.setAlignment(Qt.AlignRight) outerlayout = QVBoxLayout() outerlayout.addWidget(self.tabs) outerlayout.addSpacing(15) outerlayout.addLayout(buttonlayout) outerlayout.addWidget(self.statuslabel) self.setLayout(outerlayout) self.current_report = False self.dframe = False self.reportcombochange() self.combochange() self.setWindowTitle('PIEthon: logged in as ' + self.username) self.setWindowIcon( QIcon(functions.resource_path('resources\\PIEcon.png'))) #style things self.setStyleSheet( open(functions.resource_path("resources\\iu_stylesheet.qss"), "r").read()) self.show() def center(self): qr = self.frameGeometry() cp = QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def statusUpdate(self, newstat): #print('in status update') self.statuslabel.setText(newstat) QCoreApplication.processEvents() def startdatechange(self): self.startcombo.setCurrentIndex(0) self.startlabel.setText("Start Date: " + self.startcal.selectedDate().toString()) self.startreplabel.setText("Start Date: " + self.startcal.selectedDate().toString()) self.startrepcal.setSelectedDate(self.startcal.selectedDate()) def enddatechange(self): self.endcombo.setCurrentIndex(0) self.endlabel.setText("End Date: " + self.endcal.selectedDate().toString()) self.endreplabel.setText("End Date: " + self.endcal.selectedDate().toString()) self.endrepcal.setSelectedDate(self.endcal.selectedDate()) def startrepdatechange(self): self.startrepcombo.setCurrentIndex(0) self.startreplabel.setText("Start Date: " + self.startrepcal.selectedDate().toString()) self.startlabel.setText("Start Date: " + self.startrepcal.selectedDate().toString()) self.startcal.setSelectedDate(self.startrepcal.selectedDate()) def endrepdatechange(self): self.endrepcombo.setCurrentIndex(0) self.endreplabel.setText("End Date: " + self.endrepcal.selectedDate().toString()) self.endlabel.setText("End Date: " + self.endrepcal.selectedDate().toString()) self.endcal.setSelectedDate(self.endrepcal.selectedDate()) def startcomboselect(self): self.startrepcombo.setCurrentIndex(self.startcombo.currentIndex()) conv = self.semesters[self.startcombo.currentText()].getStart() if conv == '': return self.startlabel.setText("Start Date: " + conv.strftime('%a %b %d %Y')) self.startreplabel.setText("Start Date: " + conv.strftime('%a %b %d %Y')) self.startcal.setSelectedDate(conv) self.startrepcal.setSelectedDate(conv) def endcomboselect(self): self.endrepcombo.setCurrentIndex(self.endcombo.currentIndex()) conv = self.semesters[self.endcombo.currentText()].getEnd() if conv == '': return self.endlabel.setText("End Date: " + conv.strftime('%a %b %d %Y')) self.endreplabel.setText("End Date: " + conv.strftime('%a %b %d %Y')) self.endcal.setSelectedDate(conv) self.endrepcal.setSelectedDate(conv) def startrepcomboselect(self): self.startcombo.setCurrentIndex(self.startrepcombo.currentIndex()) conv = self.semesters[self.startrepcombo.currentText()].getStart() if conv == '': return self.startreplabel.setText("Start Date: " + conv.strftime('%a %b %d %Y')) self.startlabel.setText("Start Date: " + conv.strftime('%a %b %d %Y')) self.startrepcal.setSelectedDate(conv) self.startcal.setSelectedDate(conv) def endrepcomboselect(self): self.endcombo.setCurrentIndex(self.endrepcombo.currentIndex()) conv = self.semesters[self.endrepcombo.currentText()].getEnd() if conv == '': return self.endreplabel.setText("End Date: " + conv.strftime('%a %b %d %Y')) self.endlabel.setText("End Date: " + conv.strftime('%a %b %d %Y')) self.endrepcal.setSelectedDate(conv) self.endcal.setSelectedDate(conv) def reportcombochange(self): self.current_report = report.report_list[ self.reportdrop.currentIndex()] self.descbox.setText(self.current_report.description) self.reportactive.setText(str(self.current_report.active)) self.reportauthor.setText(str(self.current_report.author)) def combochange(self): datatype = self.dataoptions.get(self.datacombo.currentText()) if (datatype is None): return if (len(datatype.createdbyDict) > 1): self.usernamecombo.clear() self.usernamecombo.addItems(datatype.createdbyDict.keys()) self.usernamecombo.setEnabled(True) if datatype.createdbyPost: self.usernamelabel.setText("Created By (POST): ") else: self.usernamelabel.setText("Created By: ") else: self.usernamelabel.setText("Created By: ") self.usernamecombo.clear() self.usernamecombo.setEnabled(False) if (len(datatype.locationDict) > 1): self.locationcombo.clear() self.locationcombo.addItems(datatype.locationDict.keys()) self.locationcombo.setEnabled(True) if datatype.locationPost: self.locationlabel.setText("Location (POST): ") else: self.locationlabel.setText("Location: ") else: self.locationlabel.setText("Location: ") self.locationcombo.clear() self.locationcombo.setEnabled(False) if (len(datatype.statusDict) > 1): self.statuscombo.clear() self.statuscombo.addItems(datatype.statusDict) self.statuscombo.setEnabled(True) if datatype.statusPost: self.statuslabels.setText("Status (POST):") else: self.statuslabels.setText("Status:") else: self.statuslabels.setText("Status:") self.statuscombo.clear() self.statuscombo.setEnabled(False) if (len(datatype.categoryDict) > 1): self.categorycombo.clear() self.categorycombo.addItems(datatype.categoryDict.keys()) self.categorycombo.setEnabled(True) if datatype.categoryPost: self.categorylabel.setText("Category (POST):") else: self.categorylabel.setText("Category:") else: self.categorylabel.setText("Category:") self.categorycombo.clear() self.categorycombo.setEnabled(False) if (len(datatype.assignedToDict) > 1): self.assignedcombo.clear() self.assignedcombo.addItems(datatype.assignedToDict.keys()) self.assignedcombo.setEnabled(True) if datatype.assignedToPost: self.assignedlabel.setText("Assigned To (POST):") else: self.assignedlabel.setText("Assigned To:") else: self.assignedlabel.setText("Assigned To:") self.assignedcombo.clear() self.assignedcombo.setEnabled(False) self.endcal.setEnabled(datatype.allowDates) self.startcal.setEnabled(datatype.allowDates) self.startcombo.setEnabled(datatype.allowDates) self.endcombo.setEnabled(datatype.allowDates) def completed(self): self.statusUpdate('Ready') self.current_report.reset() if self.datecheck() or self.dframe is False: return if (self.tabs.currentIndex() == 0): self.mainwind = previewGui.preview( self.dframe, self.datacombo.currentText(), self.startcal.selectedDate().toPyDate(), self.endcal.selectedDate().toPyDate()) self.mainwind.show() self.dframe = False self.dataoptions.get(self.datacombo.currentText()).reset() self.current_report.reset() self.statusUpdate('Ready') def datecheck(self): return ((self.startcal.selectedDate().daysTo( self.endcal.selectedDate()) < 0) or (self.startrepcal.selectedDate().daysTo( self.endrepcal.selectedDate()) < 0))
class SearchView(QWidget): def __init__(self): super(SearchView, self).__init__() self._rowCount = 20 self._table = {} self._lock = threading.Lock() self.__initView__() self._searchEdit.setFocus() def __initView__(self): grid = QVBoxLayout(self) grid.addLayout(self.__initHead__(), Qt.AlignTop) grid.addLayout(self.__initContent__()) grid.addLayout(self.__initTail__(), Qt.AlignBottom) def __initHead__(self): self._searchEdit = LineEdit(iconUrl=getResourcePath() + "/svg/search2.svg") self._searchErrLabel = Label('', LabelStyle.SearchErr) self._searchErrLabel.hide() layout = QVBoxLayout() layout.addWidget(self._searchEdit) layout.addWidget(self._searchErrLabel) layout.setContentsMargins(0, 0, 0, 5) return layout def __initTail__(self): self._trackQualityComboBox = ComboBox([], 150) self._videoQualityComboBox = ComboBox([], 150) self._prePageBtn = PushButton('', ButtonStyle.PrePage) self._nextPageBtn = PushButton('', ButtonStyle.NextPage) self._pageIndexEdit = LineEdit('') self._pageIndexEdit.setAlignment(Qt.AlignCenter) self._pageIndexEdit.setEnabled(False) self._downloadBtn = PushButton('Download', ButtonStyle.Primary) layout = QHBoxLayout() layout.addWidget(Label('Track:')) layout.addWidget(self._trackQualityComboBox) layout.addWidget(Label('Video:')) layout.addWidget(self._videoQualityComboBox) layout.addStretch(1) layout.addWidget(self._prePageBtn) layout.addWidget(self._pageIndexEdit) layout.addWidget(self._nextPageBtn) layout.addWidget(self._downloadBtn) return layout def __initContent__(self): self._tabWidget = QTabWidget(self) self._tabWidget.addTab(self.__initTable__(Type.Album), "ALBUM") self._tabWidget.addTab(self.__initTable__(Type.Track), "TRACK") self._tabWidget.addTab(self.__initTable__(Type.Video), "VIDEO") self._tabWidget.addTab(self.__initTable__(Type.Playlist), "PLAYLIST") layout = QVBoxLayout() layout.addWidget(self._tabWidget) return layout def __initTable__(self, stype: Type): columnHeads = [] columnWidths = [] if stype == Type.Album: columnHeads = [ '#', ' ', ' ', 'Title', 'Artists', 'Release', 'Duration' ] columnWidths = [50, 60, 60, 400, 200, 200] elif stype == Type.Track: columnHeads = ['#', ' ', 'Title', 'Album', 'Artists', 'Duration'] columnWidths = [50, 60, 400, 200, 200] elif stype == Type.Video: columnHeads = ['#', ' ', ' ', 'Title', 'Artists', 'Duration'] columnWidths = [50, 60, 60, 400, 200, 200] elif stype == Type.Playlist: columnHeads = ['#', ' ', 'Title', 'Artist', 'Duration'] columnWidths = [50, 60, 400, 200, 200] self._table[stype] = TableWidget(columnHeads, self._rowCount) for index, width in enumerate(columnWidths): self._table[stype].setColumnWidth(index, width) return self._table[stype] def __clearTableRowItems__(self, stype: Type, fromIndex: int): endIndex = self._rowCount - 1 columnNum = self._table[stype].columnCount() while fromIndex <= endIndex: for colIdx in range(0, columnNum): self._table[stype].addItem(fromIndex, colIdx, '') fromIndex += 1 def setTableItems(self, stype: Type, indexOffset: int, result: tidal_dl.model.SearchResult): if stype == Type.Album: items = result.albums.items datas = [] for index, item in enumerate(items): rowData = [ str(index + 1 + indexOffset), QUrl(API.getCoverUrl(item.cover)), API.getFlag(item, Type.Album, True), item.title, item.artists[0].name, str(item.releaseDate), getDurationString(item.duration) ] datas.append(rowData) elif stype == Type.Track: items = result.tracks.items datas = [] for index, item in enumerate(items): rowData = [ str(index + 1 + indexOffset), API.getFlag(item, Type.Track, True), item.title, item.album.title, item.artists[0].name, getDurationString(item.duration) ] datas.append(rowData) elif stype == Type.Video: items = result.videos.items datas = [] for index, item in enumerate(items): rowData = [ str(index + 1 + indexOffset), QUrl(API.getCoverUrl(item.imageID)), API.getFlag(item, Type.Video, True), item.title, item.artists[0].name, getDurationString(item.duration) ] datas.append(rowData) elif stype == Type.Playlist: items = result.playlists.items datas = [] for index, item in enumerate(items): rowData = [ str(index + 1 + indexOffset), QUrl(API.getCoverUrl(item.squareImage)), item.title, '', getDurationString(item.duration) ] datas.append(rowData) for index, rowData in enumerate(datas): for colIdx, obj in enumerate(rowData): self._table[stype].addItem(index, colIdx, obj) self.__clearTableRowItems__(stype, len(items)) self._table[stype].viewport().update() def getSearchText(self): return self._searchEdit.text() def setSearchErrmsg(self, text: str): self._searchErrLabel.setText(text) if text != '': self._searchErrLabel.show() else: self._searchErrLabel.hide() def setPageIndex(self, index, sum): self._pageIndexEdit.setText(str(index) + '/' + str(sum)) def getPageIndex(self) -> (int, int): nums = self._pageIndexEdit.text().split('/') return int(nums[0]), int(nums[1]) def getSelectedTabIndex(self): return self._tabWidget.currentIndex() def getSelectedTableIndex(self, stype: Type): array = self._table[stype].selectedIndexes() if len(array) <= 0: return -1 return array[0].row() def setTrackQualityItems(self, items: list, curItem=None): self._trackQualityComboBox.setItems(items) if curItem is not None: self._trackQualityComboBox.setCurrentText(curItem) def setVideoQualityItems(self, items: list, curItem=None): self._videoQualityComboBox.setItems(items) if curItem is not None: self._videoQualityComboBox.setCurrentText(curItem) def getTrackQualityText(self): return self._trackQualityComboBox.currentText() def getVideoQualityText(self): return self._videoQualityComboBox.currentText() def connectButton(self, name: str, func): if name == 'search': self._searchEdit.returnPressed.connect(func) elif name == 'prePage': self._prePageBtn.clicked.connect(func) elif name == 'nextPage': self._nextPageBtn.clicked.connect(func) elif name == 'download': self._downloadBtn.clicked.connect(func) def connectTab(self, func): self._tabWidget.currentChanged.connect(func) def connectQualityComboBox(self, name: str, func): if name == 'track': self._trackQualityComboBox.currentIndexChanged.connect(func) else: self._videoQualityComboBox.currentIndexChanged.connect(func)
class ShowTest(QMainWindow): def __init__(self): super().__init__() self.initUI() def initUI(self): try: self.info = [] self.data = loads(crypt(open('test.UT', encoding='UTF-16').read())) self.tab = QTabWidget() self.setCentralWidget(self.tab) self.set_tab() self.end_btn = QPushButton('Завершить работу', self) self.end_btn.clicked.connect(self.finish_test) self.tab.addTab(self.end_btn, 'Завершить работу') except Exception as e: print(e) def set_tab(self): for i in range(len(self.data)): self.info.append({}) self.info[i]['page'] = QuestPage() self.info[i]['page'].back_btn.clicked.connect(self.switch) self.info[i]['page'].fwd_btn.clicked.connect(self.switch) self.info[i]['page'].question.setPlainText( self.data[i]['question']) n = len(self.data[i]['vars']) if self.data[i]['type'] == 0: self.info[i]['btns'] = [] for j in range(n): hbox = QHBoxLayout() rb = QRadioButton() self.info[i]['btns'].append(rb) hbox.addWidget(rb) label = QLabel(self.data[i]['vars'][j]) hbox.addWidget(label) hbox.addStretch() if j == n - 1 and j % 2 == 0: self.info[i]['page'].verticalLayout_1.addLayout(hbox) else: if j % 2 == 0: self.info[i]['page'].verticalLayout_2.addLayout( hbox) else: self.info[i]['page'].verticalLayout_3.addLayout( hbox) elif self.data[i]['type'] == 1: self.info[i]['btns'] = [] for j in range(n): hbox = QHBoxLayout() chb = QCheckBox() self.info[i]['btns'].append(chb) hbox.addWidget(chb) label = QLabel(self.data[i]['vars'][j]) hbox.addWidget(label) hbox.addStretch() if j == n - 1 and j % 2 == 0: self.info[i]['page'].verticalLayout_1.addLayout(hbox) else: if j % 2 == 0: self.info[i]['page'].verticalLayout_2.addLayout( hbox) else: self.info[i]['page'].verticalLayout_3.addLayout( hbox) else: self.info[i]['page'].verticalLayout_1.addWidget(QLineEdit()) self.tab.addTab(self.info[i]['page'], str(i + 1)) def switch(self): if self.sender().text() == 'Назад': self.tab.setCurrentIndex(self.tab.currentIndex() - 1) else: self.tab.setCurrentIndex(self.tab.currentIndex() + 1) def finish_test(self): try: for i in range(len(self.info)): if self.data[i]['type'] == 0: for j in self.info[i]['btns']: if self.info[i]['btns'][j].isChecked: if j == int(self.data[i]['rightAnswers']): print('OK') else: print('F**K YOU') except Exception as e: print(e)
class ImportData(QWidget): def __init__(self, parent, name): font = config.font super(QWidget, self).__init__(parent, Qt.Window) self.setWindowTitle('Import') self.setFixedSize(850, 512) self.move(0, 50) self.tabs = QTabWidget(self) self.label1 = QLabel(self) self.label2 = QLabel(self) self.label1.resize(600, 512) self.label2.resize(600, 512) self.tabs.resize(600, 512) self.tabs.addTab(self.label1, "Normal") self.tabs.addTab(self.label2, "Polar") self.textbox = QLineEdit(self) self.textbox.setText("-90") self.textbox.resize(31, 22) self.textbox.move(640, 20) self.textbox.setFont(QFont(font, 12)) self.textbox.setMaxLength(3) self.data = AnalyzeData(self, name) btnsave = QPushButton('Save', self) btnsave.resize(93, 28) btnsave.move(740, 470) btnsave.setFont(QFont(font, 12)) btnsave.clicked.connect(self.save_analyze) btn = QPushButton('Analyze', self) btn.resize(93, 28) btn.move(620, 470) btn.setFont(QFont(font, 12)) btn.clicked.connect(self.analyze_button_clicked) self.interpolation = QCheckBox("Use interpolation", self) self.interpolation.resize(121, 20) self.interpolation.move(690, 20) self.interpolation.setFont(QFont(font, 10)) phi = QLabel('φ :', self) phi.resize(31, 21) phi.move(610, 20) phi.setFont(QFont(font, 12)) degrees = QLabel('°', self) degrees.resize(16, 16) degrees.move(672, 20) degrees.setFont(QFont(font, 12)) self.direction = QLabel('', self) self.direction.resize(61, 21) self.direction.move(690, 50) self.direction.setFont(QFont(font, 12)) max_direction = QLabel("Direction :", self) max_direction.resize(81, 21) max_direction.move(610, 50) max_direction.setFont(QFont(font, 12)) zero = QLabel("Zeros :", self) zero.setFont(QFont(font, 12)) zero.resize(55, 16) zero.move(610, 110) self.zero = QListWidget(self) self.zero.setFont(QFont(font, 12)) self.zero.resize(91, 81) self.zero.move(670, 80) self.main_length = QLabel('', self) self.main_length.setFont(QFont(font, 12)) self.main_length.resize(51, 16) self.main_length.move(640, 170) theta = QLabel('θ₀ :', self) theta.setFont(QFont(font, 14)) theta.move(610, 170) theta.resize(31, 16) theta3dB = QLabel('θ₀̣₅:', self) theta3dB.setFont(QFont(font, 14)) theta3dB.move(610, 200) theta3dB.resize(31, 16) self.main_length_3dB = QLabel('', self) self.main_length_3dB.setFont(QFont(font, 12)) self.main_length_3dB.move(640, 200) self.main_length_3dB.resize(51, 16) def analyze_button_clicked(self): self.zero.clear() phi = int(self.textbox.text()) phi = phi - (phi % 5) if phi > 90: phi = 90 if phi < -90: phi = -90 self.textbox.setText(str(phi)) if self.interpolation.checkState(): self.data.delta = 1 else: self.data.delta = 0 self.data.analyze(phi) for phi in self.data.get_zeros(): self.zero.addItem(phi) self.data.show('cache/tmp/normal.png', 0) self.data.show('cache/tmp/polar.png', 1) pixmap1 = QPixmap('cache/tmp/normal.png') pixmap2 = QPixmap('cache/tmp/polar.png') self.label1.setPixmap(pixmap1) self.label2.setPixmap(pixmap2) self.main_length.setText(str(self.data.get_length()) + "°") self.main_length_3dB.setText(str(self.data.get_length_3dB()) + "°") self.direction.setText( str(int(self.data.get_direction_of_maximum()[0])) + "°") def closeEvent(self, event): if config.system == "Windows": system('del cache/tmp/normal.png') system('del cache/tmp/polar.png') elif config.system == "Linux": system('rm cache/tmp/normal.png') system('rm cache/tmp/polar.png') def save_analyze(self): print(Path('cache/tmp/normal.png')) if self.tabs.currentIndex(): fileName, _ = QFileDialog.getSaveFileName( self, '', "polar(" + self.textbox.text() + ").png", '*.png') if fileName: copyfile(Path('cache/tmp/polar.png'), fileName) else: fileName, _ = QFileDialog.getSaveFileName( self, '', "normal(" + self.textbox.text() + ").png", '*.png') if fileName: copyfile(Path('cache/tmp/normal.png'), fileName)
class StudentGroupsTabs(QWidget): def __init__(self, parent, student_listings=None): super().__init__(parent) if student_listings is None: self.student_listings = students.StudentListings() inserted_group = students.StudentGroup(0, 'INSERTED') self.student_listings.create_listing(inserted_group) else: self.student_listings = student_listings layout = QVBoxLayout(self) self.setLayout(layout) self.tabs = QTabWidget(self) # Special tab for creating a new group: self.tabs.addTab(QWidget(), ' + ') # Group 0 is special: don't show it for listing in self.student_listings[1:]: self._add_group_tab(listing) # At least one normal group needs to be present: if len(self.student_listings) == 1: self._create_default_group() button_load = QPushButton(_('Add students from file'), parent=self) button_new_student = QPushButton( QIcon(utils.resource_path('new_id.svg')), _('New student'), parent=self) button_remove = QPushButton( QIcon(utils.resource_path('discard.svg')), _('Remove group'), parent=self) layout.addWidget(self.tabs) layout.addWidget(button_load) layout.addWidget(button_new_student) layout.addWidget(button_remove) layout.setAlignment(button_load, Qt.AlignHCenter) layout.setAlignment(button_new_student, Qt.AlignHCenter) layout.setAlignment(button_remove, Qt.AlignHCenter) self.tabs.setCurrentIndex(0) self._active_tab = 0 self.tabs.currentChanged.connect(self._tab_changed) self.tabs.tabBarDoubleClicked.connect(self._rename_group) button_load.clicked.connect(self._load_students) button_new_student.clicked.connect(self._new_student) button_remove.clicked.connect(self._remove_group) def _load_students(self): index = self.tabs.currentIndex() file_name, __ = QFileDialog.getOpenFileName( self, _('Select the student list file'), '', FileNameFilters.student_list, None, QFileDialog.DontUseNativeDialog) try: if file_name: with students.StudentReader.create(file_name) as reader: student_list = list(reader.students()) # Flag duplicate student ids: warn_duplicates = False for s in self.student_listings.find_duplicates(student_list): s.is_duplicate = True warn_duplicates = True if warn_duplicates: QMessageBox.warning( self, _('Importing a student list'), _('Some student ids are already in the list. ' 'Remove them or cancel the import operation.')) column_map = reader.column_map.normalize() preview_dialog = DialogPreviewStudents( self, student_list, column_map) result = preview_dialog.exec_() if result == QMessageBox.Accepted: self.tabs.widget(index).add_students(student_list) except Exception as e: QMessageBox.critical( self, _('Error in student list'), file_name + '\n\n' + str(e)) def _new_student(self): index = self.tabs.currentIndex() dialog = NewStudentDialog( self.student_listings, group_index=index, parent=self) student = dialog.exec_() if student is not None: self.tabs.widget(index).listing_updated() def _remove_group(self): index = self.tabs.currentIndex() if len(self.student_listings[index + 1]) > 0: result = QMessageBox.warning( self, _('Warning'), _('This group and its students will be removed. ' 'Are you sure you want to continue?'), QMessageBox.Yes | QMessageBox.No, QMessageBox.No) remove = (result == QMessageBox.Yes) else: remove = True if remove: try: self.student_listings.remove_at(index + 1) if len(self.student_listings) > 1: if index == self.tabs.count() - 2: self.tabs.setCurrentIndex(index - 1) else: self.tabs.setCurrentIndex(index + 1) else: self._create_default_group() self.tabs.setCurrentIndex(1) self.tabs.removeTab(index) except students.CantRemoveGroupException: QMessageBox.critical( self, _('Error'), _('This group cannot be removed because ' 'exams have been graded for some of its students.')) def _add_group_tab(self, listing, show=False): self.tabs.insertTab( self.tabs.count() - 1, GroupWidget(listing, self), listing.group.name ) if show: self.tabs.setCurrentIndex(self.tabs.count() - 2) def _new_group(self): group_name = GroupNameDialog(parent=self).exec_() if group_name is not None: group = students.StudentGroup(None, group_name) listing = self.student_listings.create_listing(group) self._add_group_tab(listing, show=True) else: self.tabs.setCurrentIndex(self._active_tab) def _create_default_group(self): group = students.StudentGroup(None, _('Students')) listing = self.student_listings.create_listing(group) self._add_group_tab(listing, show=True) def _rename_group(self, index): name = self.student_listings[index + 1].group.name new_name = GroupNameDialog(group_name=name, parent=self).exec_() if new_name is not None and new_name != name: self.student_listings[index + 1].rename(new_name) self.tabs.setTabText(index, new_name) def _tab_changed(self, index): if index == self.tabs.count() - 1: # The last (special) tab has been activated: create a new group self._new_group() self._active_tab = self.tabs.currentIndex()
class MainWindow(QDialog, window1.Ui_PyDialog): def __init__(self, parent=None): global arguments, return_keyword self.event_entered = False self.event2_entered = False super(MainWindow, self).__init__(parent) self.setupUi(self) if arguments.stayontop: from PyQt5.QtCore import Qt self.setWindowFlags(Qt.WindowStaysOnTopHint) self.label.setSizePolicy(QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.MinimumExpanding)) self.button_ids = ["details_button", "ok_button", "yes_button", "no_button", "continue_button", "save_button", "cancel_button"] self.button_names = { "details_button":_("Details"), "ok_button":_("Ok"), "yes_button":_("Yes"), "no_button":_("No"), "continue_button":_("Continue"), "save_button":_("Save"), "cancel_button":_("Cancel") } self.button_values = {} self.create_elements() self.word_wrap() def word_wrap(self): if self.label.sizeHint().width() > 600: self.label.setWordWrap(True) self.label.setScaledContents(True) self.label.setMinimumWidth(600) def create_elements(self): self.active_buttons = dict((e, False) for e in self.button_names) self.progressbar_cancelled = False self.hide_unused_elements() self.init_conf() self.create_buttons() self.set_button_labels() noab = len(list(filter(lambda x: self.active_buttons[x], self.active_buttons))) self.reject_value = noab - 1 def hide_unused_elements(self): """ Hide the unused elements """ global arguments if not arguments.forkedprogressbar: self.progressBar.hide() if not arguments.slider: self.horizontalSlider.hide() if not arguments.combobox: self.comboBox.hide() if not arguments.inputbox and not arguments.password: self.lineEdit.hide() if not arguments.combobox and not arguments.password: self.label_2.hide() def init_conf(self): """ Initial configurations (buttons and labels) """ global arguments if arguments.title: self.setWindowTitle(arguments.title) if arguments.icon: from PyQt5.QtGui import QIcon icon = QIcon(arguments.icon) self.setWindowIcon(icon) if arguments.yesno or arguments.warningyesno: self.enable_buttons(["yes_button", "no_button"]) if arguments.yesno: self.label.setText(arguments.yesno) else: self.label.setText(arguments.warningyesno) elif arguments.yesnocancel or arguments.warningyesnocancel: self.enable_buttons(["yes_button", "no_button", "cancel_button"]) if arguments.yesnocancel: self.label.setText(arguments.yesnocancel) else: self.label.setText(arguments.warningyesnocancel) elif arguments.sorry or arguments.error or arguments.msgbox: self.enable_buttons(["ok_button"]) if arguments.sorry: self.label.setText(arguments.sorry) elif arguments.error: self.label.setText(arguments.error) else: self.label.setText(arguments.msgbox) elif arguments.detailedsorry or arguments.detailederror: self.enable_buttons(["details_button", "ok_button"]) if arguments.detailedsorry: self.label.setText(arguments.detailedsorry[0]) self.details = arguments.detailedsorry[1] else: self.label.setText(arguments.detailederror[0]) self.details = arguments.detailederror[1] elif arguments.warningcontinuecancel: self.enable_buttons(["continue_button", "cancel_button"]) self.label.setText(arguments.warningcontinuecancel) elif arguments.forkedprogressbar: self.label.setText(arguments.forkedprogressbar[0]) if len(arguments.forkedprogressbar) > 1: self.progressBar.setMaximum(int(arguments.forkedprogressbar[1])) elif arguments.slider: self.enable_buttons(["ok_button", "cancel_button"]) self.label.setText(arguments.slider[0]) if len(arguments.slider) > 1: self.horizontalSlider.setMinimum(int(arguments.slider[1])) if len(arguments.slider) > 2: self.horizontalSlider.setMaximum(int(arguments.slider[2])) if len(arguments.slider) > 3: self.horizontalSlider.setSingleStep(int(arguments.slider[3])) self.horizontalSlider.setPageStep(int(arguments.slider[3])) elif arguments.inputbox: self.enable_buttons(["ok_button", "cancel_button"]) self.label.setText(arguments.inputbox[0]) if len(arguments.inputbox) > 1: self.lineEdit.setText(arguments.inputbox[1]) elif arguments.password: self.enable_buttons(["ok_button", "cancel_button"]) self.lineEdit.setEchoMode(2) self.label.setText(arguments.password[0]) self.label_2.setText(_("Password:"******"ok_button", "cancel_button"]) def create_scrollarea(self, scrollLayout): from PyQt5.QtWidgets import QHBoxLayout, QWidget, QScrollArea from PyQt5.QtCore import Qt scrollWidget = QWidget() scrollAreaLayout = QHBoxLayout() scrollWidget.setLayout(scrollLayout) #hscrollbar = QScrollBar() #vscrollbar = QScrollBar() scrollArea = QScrollArea() #scrollArea.setHorizontalScrollBar(hscrollbar) #scrollArea.setVerticalScrollBar(vscrollbar) #scrollArea.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff) #scrollArea.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) self.set_scrollarea_height(scrollArea) scrollArea.setWidget(scrollWidget) scrollAreaLayout.addWidget(scrollArea) #scrollAreaLayout.addWidget(vscrollbar) return scrollAreaLayout #, hscrollbar def set_scrollarea_height(self, scrollarea): if arguments.checklist: elements = (len(arguments.checklist)-1) / 3 elif arguments.radiolist: elements = (len(arguments.radiolist)-1) / 3 elif arguments.menu: elements = (len(arguments.menu)-1) / 2 if elements < 3: pass elif elements == 3: scrollarea.setMinimumHeight(90) elif elements == 4: scrollarea.setMinimumHeight(115) else: scrollarea.setMinimumHeight(140) def add_checkboxes(self, tab=False): from PyQt5.QtWidgets import QCheckBox, QVBoxLayout scrollLayout = QVBoxLayout() checkboxes = [] if tab: i = 2 name = "tab" else: i = 1 name = "checklist" l = len(arguments.__dict__[name]) while i < l: checkbox = QCheckBox(arguments.__dict__[name][i+1]) if arguments.__dict__[name][i+2].lower() in ["true", "on"]: checkbox.setCheckState(2) checkboxes.append({"box":checkbox, "result":arguments.__dict__[name][i]}) scrollLayout.addWidget(checkbox) i += 3 return scrollLayout, checkboxes def add_radiobuttons(self, tab=False): from PyQt5.QtWidgets import QRadioButton, QButtonGroup, QVBoxLayout scrollLayout = QVBoxLayout() buttonGroup = QButtonGroup() buttongroup_results = {} i = 1 if tab: name = "tab" i = 2 elif arguments.radiolist: name = "radiolist" elif arguments.menu: name = "menu" arglen = len(arguments.__dict__[name]) while i < arglen: if arguments.radiolist: radiobutton = QRadioButton(arguments.__dict__[name][i+1]) buttongroup_results[radiobutton] = arguments.__dict__[name][i] if arguments.__dict__[name][i+2].lower() in ["true", "on"]: radiobutton.setChecked(True) i += 3 else: radiobutton = QRadioButton(arguments.__dict__[name][i+1]) buttongroup_results[radiobutton] = arguments.__dict__[name][i] if i == 1: radiobutton.setChecked(True) i += 2 scrollLayout.addWidget(radiobutton) buttonGroup.addButton(radiobutton) return scrollLayout, buttonGroup, buttongroup_results def set_button_labels(self): """Set the button labels""" global arguments if arguments.yeslabel and self.active_buttons["yes_button"]: self.buttons["yes_button"].setText(arguments.yeslabel) if arguments.nolabel and self.active_buttons["no_button"]: self.buttons["no_button"].setText(arguments.nolabel) if arguments.cancellabel and self.active_buttons["cancel_button"]: self.buttons["cancel_button"].setText(arguments.cancellabel) if arguments.continuelabel and self.active_buttons["continue_button"]: self.buttons["continue_button"].setText(arguments.continuelabel) def create_buttons(self): global arguments self.buttons = {} i = 0 for button_id in self.button_ids: if self.active_buttons[button_id]: self.buttons[button_id] = QPushButton(self.button_names[button_id]) self.horizontalLayout.addWidget(self.buttons[button_id]) if button_id == "details_button": self.buttons["details_button"].clicked.connect(self.details_button_clicked) elif button_id == "cancel_button": self.buttons[button_id].clicked.connect(self.reject) else: self.button_values[button_id] = i exec("self.buttons[button_id].clicked.connect(self."+button_id+"_clicked)") i += 1 def print_checkboxes(self): if arguments.separateoutput: fs = '{}' data_end = "\n" else: fs = '"{}"' data_end = " " for e in self.checkboxes: if e["box"].isChecked(): print(fs.format(e["result"]), end=data_end) if arguments.tab: for e in self.checkboxes2: if e["box"].isChecked(): print(fs.format(e["result"]), end=data_end) def get_checked_radiobutton(self): n = "" if arguments.tab: if self.tabwidget.currentIndex() == 1: n = "2" radiobutton_name = self.__dict__["buttonGroup"+n].checkedButton() print(self.__dict__["buttongroup_results"+n][radiobutton_name]) def ok_button_clicked(self): if arguments.slider: print(self.horizontalSlider.value()) elif arguments.inputbox or arguments.password: print(self.lineEdit.text()) elif arguments.checklist: self.print_checkboxes() elif arguments.radiolist or arguments.menu: self.get_checked_radiobutton() print(return_keyword+str(self.button_values["ok_button"])+">") self.done(0) def yes_button_clicked(self): print(return_keyword+str(self.button_values["yes_button"])+">") self.done(0) def no_button_clicked(self): print(return_keyword+str(self.button_values["no_button"])+">") self.done(0) def continue_button_clicked(self): print(return_keyword+str(self.button_values["continue_button"])+">") self.done(0) def save_button_clicked(self): print(return_keyword+str(self.button_values["save_button"])+">") self.done(0) def reject(self): print(return_keyword+str(self.reject_value)+">") self.done(0) def enable_buttons (self, button_list): for button in button_list: self.active_buttons[button] = True def details_button_clicked (self): self.label.setText(self.label.text() + '\n\n' + self.details) self.buttons["details_button"].setDisabled(True) def progressbar_cancel_clicked(self): self.progressbar_cancelled = True def showCancelButton(self): if not "cancel_button" in self.buttons: self.buttons["cancel_button"] = QPushButton(self.button_names["cancel_button"]) self.buttons["cancel_button"].clicked.connect(self.progressbar_cancel_clicked) self.horizontalLayout.addWidget(self.buttons["cancel_button"]) self.progressbar_cancelled = False self.buttons["cancel_button"].show()
class AnalyzerWidget(QWidget): def __init__(self): super().__init__() hbox = QHBoxLayout() # ----------------------------- # Create frames # ----------------------------- # 1. Plot frame: plotFrame = QFrame() plotFrame.setFrameShape(QFrame.Panel | QFrame.Raised) plotFrame.setStyleSheet(stylesheet_qframe1()) figure = plt.figure() canvas = FigureCanvas(figure) ln = 100000 data = [random.random() for i in range(ln)] data = np.cumsum(data - np.mean(data)) ax = figure.add_subplot(111) plt.subplots_adjust(left=0.00, bottom=0.002, right=1, top=1, wspace=0, hspace=0) ax.plot(data) ax.grid() ax.set_facecolor('#C4C4C4') ax.grid(color='#B80C09', linestyle=':') plt.xlim([0, len(data)]) canvas.draw() pLayout = QVBoxLayout() pLayout.addWidget(canvas) plotFrame.setLayout(pLayout) plotFrame.setContentsMargins(0, 0, 0, 0) # 2. Horizontal sliders frame: sHbox = QVBoxLayout() slider1h = QSlider(Qt.Horizontal, self) slider1h.setStyleSheet(stylesheet_h()) slider2h = QSlider(Qt.Horizontal, self) slider2h.setStyleSheet(stylesheet_h()) sHbox.addWidget(slider1h) sHbox.addWidget(slider2h) sHbox.setSpacing(0) sHbox.setContentsMargins(1, 0, 1, 0) sFrameH = QFrame() sFrameH.setFrameShape(QFrame.Panel | QFrame.Raised) sFrameH.setLayout(sHbox) # 3. Vertical sliders frame: sVbox = QHBoxLayout() slider1v = QSlider(Qt.Vertical, self) slider1v.setStyleSheet(stylesheet_v()) slider2v = QSlider(Qt.Vertical, self) slider2v.setStyleSheet(stylesheet_v()) sVbox.addWidget(slider1v) sVbox.addWidget(slider2v) sVbox.setSpacing(0) sVbox.setContentsMargins(1, 0, 1, 0) sFrameV = QFrame() sFrameV.setFrameShape(QFrame.Panel | QFrame.Raised) sFrameV.setLayout(sVbox) # 4. SlidersControl frame: cornerFrame = QFrame() btnMax1 = QPushButton('max', cornerFrame) btnMax2 = QPushButton('max', cornerFrame) btnMax1.setGeometry(1, 0, 30, 20) btnMax2.setGeometry(1, 17, 30, 20) editMax1 = QLineEdit('0', cornerFrame) editMax2 = QLineEdit('0', cornerFrame) editMax1.setGeometry(31, 0, 60, 20) editMax2.setGeometry(31, 17, 60, 20) cornerFrame.setFrameShape(QFrame.Panel | QFrame.Raised) # 5. Control Panel frame: controlFrame = QFrame() controlFrame.setFrameShape(QFrame.Panel | QFrame.Raised) controlFrame.setLineWidth(2) control_tab = QTabWidget() cTab1 = QWidget() # Tab Browser # ------------------------ cLayout1 = QVBoxLayout() cFrame1 = QFrame() self.lst_file = QListWidget() self.lst_dir = QListWidget() self.tab_files = QTabWidget() self.tab_files.addTab(self.lst_file, 'Files') self.tab_files.addTab(self.lst_dir, 'Directories') cLayout_btn = QHBoxLayout() self.btn_load = QPushButton('Load') self.btn_load.setObjectName('Load') self.btn_load.clicked.connect(self.openFileNameDialog) self.btn_info = QPushButton('Info') self.btn_info.setObjectName('Info') self.btn_info.clicked.connect(self.openFileNameDialog) self.btn_prev = QPushButton('Preview') self.btn_prev.setObjectName('Preview') self.btn_prev.clicked.connect(self.openFileNameDialog) # get open histories self.filepath = '' if os.path.exists('files_history.txt'): with open('files_history.txt', 'r') as f: files_history = f.read().splitlines() path_history = getPathList(files_history) self.filepath = path_history[0] else: files_history = list() path_history = list() for item in files_history: self.lst_file.addItem(item) for item in path_history: self.lst_dir.addItem(item) cLayout_btn.addWidget(self.btn_load) cLayout_btn.addWidget(self.btn_info) cLayout_btn.addWidget(self.btn_prev) lbl_hist = QLabel('Load data files history') cLayout1.addWidget(lbl_hist) cLayout1.addWidget(self.tab_files) cLayout1.addLayout(cLayout_btn) cFrame1.setLayout(cLayout1) lbl_obj = QLabel('Loaded objects') b_tree = QTreeView() b_tree.setAlternatingRowColors(True) b_tree_model = QStandardItemModel(0, 5) b_tree_node = b_tree_model.invisibleRootItem() branch1 = QStandardItem("a") branch1.appendRow([QStandardItem("Child A"), None]) childNode = QStandardItem("Child B") branch1.appendRow([childNode, None]) branch2 = QStandardItem("b") branch2.appendRow([QStandardItem("Child C"), None]) branch2.appendRow([QStandardItem("Child D"), None]) branch3 = QStandardItem('c') branch4 = QStandardItem('d') b_tree_node.appendRow([branch1, None]) b_tree_node.appendRow([branch2, None]) b_tree_node.appendRow([branch3, None]) b_tree_node.appendRow([branch4, None]) b_tree.setModel(b_tree_model) b_tree_model.setHeaderData(0, Qt.Horizontal, 'Name') b_tree_model.setHeaderData(1, Qt.Horizontal, 'Type') b_tree_model.setHeaderData(2, Qt.Horizontal, 'Data') b_tree_model.setHeaderData(3, Qt.Horizontal, 'Size') b_tree_model.setHeaderData(4, Qt.Horizontal, 'Modified') b_tree.setColumnWidth(0, 70) cLayout2 = QVBoxLayout() cLayout2.addWidget(lbl_obj) cLayout2.addWidget(b_tree) cFrame2 = QFrame() cFrame2.setLayout(cLayout2) cSplitBrowser = QSplitter(Qt.Vertical) cSplitBrowser.addWidget(cFrame1) cSplitBrowser.addWidget(cFrame2) cSplitBrowser.setSizes([200, 50]) cLayout = QVBoxLayout() cLayout.addWidget(cSplitBrowser) cLayout.setContentsMargins(0, 0, 0, 0) # cLayout.addWidget(cFrame2) cFrame = QFrame() cFrame.setLayout(cLayout) control_tab.addTab(cFrame, 'Browser') # Tab Work # ------------------------------- wFrame1 = QFrame() wLayout = QGridLayout() wLbl_proc = QLabel('Process type') wComboBox1 = QComboBox() w_list = [ "Signal view", "Differentiation", "Integration", "Correlation", "Allan variance", "Fitting", "Termo compensations", "Calibration", "Navigation", "Fourier transform" ] wComboBox1.setStyleSheet(stylesheet_combo2()) wComboBox1.addItems(w_list) wFrame2 = QFrame() wFrame2.setFrameShape(QFrame.Box) wLayout.addWidget(wLbl_proc, 0, 0, 1, 1) wLayout.addWidget(wComboBox1, 1, 0, 1, 1) wLayout.addWidget(wFrame2, 2, 0, 35, 1) # Frame 2 wLayout2 = QHBoxLayout() wLayout2.addWidget(QPushButton('one')) wLayout2.addWidget(QPushButton('two')) wLayout2.addWidget(QPushButton('three')) wLayout2.setAlignment(Qt.AlignTop) wLayout2.setSpacing(0) wFrame2.setLayout(wLayout2) wLayout.setContentsMargins(1, 1, 1, 1) wFrame1.setLayout(wLayout) control_tab.addTab(wFrame1, 'Work') # Tab Model # ------------------------------- cTab3 = QWidget() control_tab.addTab(cTab3, 'Model') control_layout = QVBoxLayout() control_layout.addWidget(control_tab) control_layout.setContentsMargins(0, 0, 0, 0) controlFrame.setLayout(control_layout) # 6. Log panel: LogEdit = QTextEdit('>>') # ----------------------------- # Split frames # ----------------------------- # 1. Plot + Vertical sliders: split1 = QSplitter(Qt.Horizontal) split1.addWidget(plotFrame) split1.addWidget(sFrameV) split1.setSizes([680, 20]) # 2. Horizontal sliders + SlidersControl: split2 = QSplitter(Qt.Horizontal) split2.addWidget(sFrameH) split2.addWidget(cornerFrame) split2.setSizes([700, 40]) # 3. 1 + 2 : ver_split = QSplitter(Qt.Vertical) ver_split.addWidget(split1) ver_split.addWidget(split2) ver_split.setSizes([800, 50]) # 4. 3 + Control Panel: hor_split = QSplitter(Qt.Horizontal) hor_split.addWidget(ver_split) hor_split.addWidget(controlFrame) hor_split.setSizes([700, 200]) # 5. 4 + Log frame: bottom_split = QSplitter(Qt.Vertical) bottom_split.addWidget(hor_split) bottom_split.addWidget(LogEdit) bottom_split.setSizes([700, 0]) hbox.addWidget(bottom_split) self.setLayout(hbox) QApplication.setStyle(QStyleFactory.create('cleanlooks')) def openFileNameDialog(self): # Get adress from tab lists: if self.tab_files.tabText(self.tab_files.currentIndex()) == 'Files': if self.lst_file.currentItem(): fileName = self.lst_file.currentItem().text() else: fileName = self.filepath elif self.tab_files.tabText( self.tab_files.currentIndex()) == 'Directories': if self.lst_dir.currentItem(): fileName = self.lst_dir.currentItem().text() else: fileName = self.filepath # Check adress and set default dir if it not exist if not os.path.isfile(fileName) and not os.path.isdir(fileName): fileName = self.filepath # Load file if os.path.isdir(fileName): fileName, _ = QFileDialog.getOpenFileName(self, "Select file for load", directory=fileName) print(fileName) # Get and sort history, refresh tab lists: if os.path.isfile(fileName): # create files_history if it is not exits if not os.path.exists('files_history.txt'): with open('files_history.txt', 'w') as out: out.write(fileName + '\n') else: # get all files from history with open('files_history.txt', 'r') as f: files_history = f.read().splitlines() # check file for exist in history if not fileName in files_history: with open('files_history.txt', 'a') as f: f.write(fileName + '\n') # sort history list by last open file with open('files_history.txt', 'r') as f: files_history = f.read().splitlines() del files_history[files_history.index(fileName)] files_history.insert(0, fileName) with open('files_history.txt', 'w') as f: for line in files_history: f.write("%s\n" % line) self.lst_file.clear() self.lst_dir.clear() for currFilePath in files_history: self.lst_file.addItem(currFilePath) path_history = getPathList(files_history) for curPath in path_history: self.lst_dir.addItem(curPath) # Processing file: # ---------------------------------------------- current_button = self.sender() if current_button.objectName() == "Info": try: A = ScanDataFile.scan_file(fileName) ScanDataFile.info(A) except: pass # # ----------------------------------------------- if current_button.objectName() == "Preview": try: A = LoadDataFile(fileName) if A.file_format['read']: self.wnd = ViewData(A) self.wnd.initUI() self.wnd.show() else: err = QErrorMessage(self) err.showMessage('Unreadable format of the file.') except: err = QErrorMessage(self) err.showMessage('Can not read this file.')
class RunWidget(QWidget): allTabsClosed = pyqtSignal() projectExecuted = pyqtSignal(str) fileExecuted = pyqtSignal(str) def __init__(self): QWidget.__init__(self) self.__programs = [] vbox = QVBoxLayout(self) vbox.setContentsMargins(0, 0, 0, 0) vbox.setSpacing(0) connections = ( { "target": "tools_dock", "signal_name": "executeFile", "slot": self.execute_file }, { "target": "tools_dock", "signal_name": "executeProject", "slot": self.execute_project }, { "target": "tools_dock", "signal_name": "executeSelection", "slot": self.execute_selection }, { "target": "tools_dock", "signal_name": "stopApplication", "slot": self.kill_application } ) IDE.register_signals("tools_dock", connections) self._tabs = QTabWidget() self._tabs.setTabsClosable(True) self._tabs.setMovable(True) self._tabs.setDocumentMode(True) vbox.addWidget(self._tabs) # Menu for tab self._tabs.tabBar().setContextMenuPolicy(Qt.CustomContextMenu) self._tabs.tabBar().customContextMenuRequested.connect( self._menu_for_tabbar) self._tabs.tabCloseRequested.connect(self.close_tab) IDE.register_service("run_widget", self) _ToolsDock.register_widget(translations.TR_OUTPUT, self) def install(self): ninjaide = IDE.get_service("ide") ninjaide.goingDown.connect(self._kill_processes) def _kill_processes(self): """Stop all applications""" for program in self.__programs: program.kill() def kill_application(self): """Stop application by current tab index""" index = self._tabs.currentIndex() if index == -1: return program = self.__programs[index] program.kill() def _menu_for_tabbar(self, position): menu = QMenu() close_action = menu.addAction(translations.TR_CLOSE_TAB) close_all_action = menu.addAction(translations.TR_CLOSE_ALL_TABS) close_other_action = menu.addAction(translations.TR_CLOSE_OTHER_TABS) qaction = menu.exec_(self.mapToGlobal(position)) if qaction == close_action: index = self._tabs.tabBar().tabAt(position) self.close_tab(index) elif qaction == close_all_action: self.close_all_tabs() elif qaction == close_other_action: self.close_all_tabs_except_this() def close_tab(self, tab_index): program = self.__programs[tab_index] self.__programs.remove(program) self._tabs.removeTab(tab_index) # Close process and delete OutputWidget program.main_process.close() program.outputw.deleteLater() del program.outputw if self._tabs.count() == 0: # Hide widget tools = IDE.get_service("tools_dock") tools.hide_widget(self) def close_all_tabs(self): for _ in range(self._tabs.count()): self.close_tab(0) def close_all_tabs_except_this(self): self._tabs.tabBar().moveTab(self._tabs.currentIndex(), 0) for _ in range(self._tabs.count()): if self._tabs.count() > 1: self.close_tab(1) def execute_file(self): """Execute the current file""" main_container = IDE.get_service("main_container") editor_widget = main_container.get_current_editor() if editor_widget is not None and (editor_widget.is_modified or editor_widget.file_path): main_container.save_file(editor_widget) file_path = editor_widget.file_path if file_path is None: return # Emit signal for plugin! self.fileExecuted.emit(editor_widget.file_path) extension = file_manager.get_file_extension(file_path) # TODO: Remove the IF statment and use Handlers if extension == "py": self.start_process(filename=file_path) def execute_selection(self): """Execute selected text or current line if not have a selection""" main_container = IDE.get_service("main_container") editor_widget = main_container.get_current_editor() if editor_widget is not None: text = editor_widget.selected_text().splitlines() if not text: # Execute current line text = [editor_widget.line_text()] code = [] for line_text in text: # Get part before firs '#' code.append(line_text.split("#", 1)[0]) # Join to execute with python -c command final_code = ";".join([line.strip() for line in code if line]) # Highlight code to be executed editor_widget.show_run_cursor() # Ok run! self.start_process( filename=editor_widget.file_path, code=final_code) def execute_project(self): """Execute the project marked as Main Project.""" projects_explorer = IDE.get_service("projects_explorer") if projects_explorer is None: return nproject = projects_explorer.current_project if nproject: main_file = nproject.main_file if not main_file: # Open project properties to specify the main file projects_explorer.current_tree.open_project_properties() else: # Save project files projects_explorer.save_project() # Emit a signal for plugin! self.projectExecuted.emit(nproject.path) main_file = file_manager.create_path( nproject.path, nproject.main_file) self.start_process( filename=main_file, python_exec=nproject.python_exec, pre_exec_script=nproject.pre_exec_script, post_exec_script=nproject.post_exec_script, program_params=nproject.program_params ) def start_process(self, **kwargs): # First look if we can reuse a tab fname = kwargs.get("filename") program = None for prog in self.__programs: if prog.filename == fname: if not prog.is_running(): program = prog break if program is not None: index = self.__programs.index(program) program.update(**kwargs) self._tabs.setCurrentIndex(index) program.outputw.gray_out_old_text() else: program = Program(**kwargs) # Create new output widget outputw = OutputWidget(self) program.set_output_widget(outputw) self.add_tab(outputw, program.display_name()) self.__programs.append(program) program.start() def add_tab(self, outputw, tab_text): inserted_index = self._tabs.addTab(outputw, tab_text) self._tabs.setCurrentIndex(inserted_index)
class ComboEditor(QWidget): def __init__(self, pretty_tab=True, enable_pretty=True): QWidget.__init__(self) self.setLayout(QVBoxLayout()) self.layout().setSpacing(0) self.layout().setContentsMargins(0, 0, 0, 0) self.data = b'' self.enable_pretty = enable_pretty self.tabWidget = QTabWidget() self.hexteditor = HextEditor(enable_pretty=self.enable_pretty) self.hexeditor = HexEditor() self.ppwidg = PrettyPrintWidget() self.hexteditor_ind = self.tabWidget.count() self.tabWidget.addTab(self.hexteditor, "Text") self.hexeditor_ind = self.tabWidget.count() self.tabWidget.addTab(self.hexeditor, "Hex") self.pp_ind = -1 if pretty_tab: self.pp_ind = self.tabWidget.count() self.tabWidget.addTab(self.ppwidg, "Pretty") self.tabWidget.currentChanged.connect(self._tab_changed) self.previous_tab = self.tabWidget.currentIndex() self.layout().addWidget(self.tabWidget) @pyqtSlot(int) def _tab_changed(self, i): # commit data from old tab if self.previous_tab == self.hexteditor_ind: self.data = self.hexteditor.get_bytes() if self.previous_tab == self.hexeditor_ind: self.data = self.hexeditor.get_bytes() # set up new tab if i == self.hexteditor_ind: if self.hexteditor.pretty_mode: self.hexteditor.set_bytes_highlighted(self.data) else: self.hexteditor.set_bytes(self.data) if i == self.hexeditor_ind: self.hexeditor.set_bytes(self.data) if i == self.pp_ind: self.ppwidg.set_bytes(self.data) self.ppwidg.guess_format() # update previous tab self.previous_tab = self.tabWidget.currentIndex() @pyqtSlot(bytes) def set_bytes(self, bs): self.data = bs self.tabWidget.setCurrentIndex(0) if self.tabWidget.currentIndex() == self.hexteditor_ind: self.hexteditor.set_bytes(bs) elif self.tabWidget.currentIndex() == self.hexeditor_ind: self.hexeditor.set_bytes(bs) elif self.tabWidget.currentIndex() == self.pp_ind: self.ppwidg.set_bytes(bs) @pyqtSlot(bytes) def set_bytes_highlighted(self, bs, lexer=None): self.data = bs self.tabWidget.setCurrentIndex(0) if self.enable_pretty: self.hexteditor.set_bytes_highlighted(bs, lexer=lexer) else: self.set_bytes(bs) def get_bytes(self): if self.tabWidget.currentIndex() == self.hexteditor_ind: self.data = self.hexteditor.get_bytes() elif self.tabWidget.currentIndex() == self.hexeditor_ind: self.data = self.hexeditor.get_bytes() return self.data def setReadOnly(self, ro): self.hexteditor.setReadOnly(ro) self.hexeditor.setReadOnly(ro)
class ReTextWindow(QMainWindow): def __init__(self, parent=None): QMainWindow.__init__(self, parent) self.resize(950, 700) screenRect = QDesktopWidget().screenGeometry() if globalSettings.windowGeometry: self.restoreGeometry(globalSettings.windowGeometry) else: self.move((screenRect.width()-self.width())/2, (screenRect.height()-self.height())/2) if not screenRect.contains(self.geometry()): self.showMaximized() if sys.platform.startswith('darwin'): # https://github.com/retext-project/retext/issues/198 searchPaths = QIcon.themeSearchPaths() searchPaths.append('/opt/local/share/icons') QIcon.setThemeSearchPaths(searchPaths) if globalSettings.iconTheme: QIcon.setThemeName(globalSettings.iconTheme) if QIcon.themeName() in ('hicolor', ''): if not QFile.exists(icon_path + 'document-new.png'): QIcon.setThemeName(get_icon_theme()) if QFile.exists(icon_path+'retext.png'): self.setWindowIcon(QIcon(icon_path+'retext.png')) elif QFile.exists('/usr/share/pixmaps/retext.png'): self.setWindowIcon(QIcon('/usr/share/pixmaps/retext.png')) else: self.setWindowIcon(QIcon.fromTheme('retext', QIcon.fromTheme('accessories-text-editor'))) self.tabWidget = QTabWidget(self) self.initTabWidget() self.setCentralWidget(self.tabWidget) self.tabWidget.currentChanged.connect(self.changeIndex) self.tabWidget.tabCloseRequested.connect(self.closeTab) toolBar = QToolBar(self.tr('File toolbar'), self) self.addToolBar(Qt.TopToolBarArea, toolBar) self.editBar = QToolBar(self.tr('Edit toolbar'), self) self.addToolBar(Qt.TopToolBarArea, self.editBar) self.searchBar = QToolBar(self.tr('Search toolbar'), self) self.addToolBar(Qt.BottomToolBarArea, self.searchBar) toolBar.setVisible(not globalSettings.hideToolBar) self.editBar.setVisible(not globalSettings.hideToolBar) self.actionNew = self.act(self.tr('New'), 'document-new', self.createNew, shct=QKeySequence.New) self.actionNew.setPriority(QAction.LowPriority) self.actionOpen = self.act(self.tr('Open'), 'document-open', self.openFile, shct=QKeySequence.Open) self.actionOpen.setPriority(QAction.LowPriority) self.actionSetEncoding = self.act(self.tr('Set encoding'), trig=self.showEncodingDialog) self.actionSetEncoding.setEnabled(False) self.actionReload = self.act(self.tr('Reload'), 'view-refresh', lambda: self.currentTab.readTextFromFile()) self.actionReload.setEnabled(False) self.actionSave = self.act(self.tr('Save'), 'document-save', self.saveFile, shct=QKeySequence.Save) self.actionSave.setEnabled(False) self.actionSave.setPriority(QAction.LowPriority) self.actionSaveAs = self.act(self.tr('Save as'), 'document-save-as', self.saveFileAs, shct=QKeySequence.SaveAs) self.actionNextTab = self.act(self.tr('Next tab'), 'go-next', lambda: self.switchTab(1), shct=Qt.CTRL+Qt.Key_PageDown) self.actionPrevTab = self.act(self.tr('Previous tab'), 'go-previous', lambda: self.switchTab(-1), shct=Qt.CTRL+Qt.Key_PageUp) self.actionPrint = self.act(self.tr('Print'), 'document-print', self.printFile, shct=QKeySequence.Print) self.actionPrint.setPriority(QAction.LowPriority) self.actionPrintPreview = self.act(self.tr('Print preview'), 'document-print-preview', self.printPreview) self.actionViewHtml = self.act(self.tr('View HTML code'), 'text-html', self.viewHtml) self.actionChangeEditorFont = self.act(self.tr('Change editor font'), trig=self.changeEditorFont) self.actionChangePreviewFont = self.act(self.tr('Change preview font'), trig=self.changePreviewFont) self.actionSearch = self.act(self.tr('Find text'), 'edit-find', shct=QKeySequence.Find) self.actionSearch.setCheckable(True) self.actionSearch.triggered[bool].connect(self.searchBar.setVisible) self.searchBar.visibilityChanged.connect(self.searchBarVisibilityChanged) self.actionPreview = self.act(self.tr('Preview'), shct=Qt.CTRL+Qt.Key_E, trigbool=self.preview) if QIcon.hasThemeIcon('document-preview'): self.actionPreview.setIcon(QIcon.fromTheme('document-preview')) elif QIcon.hasThemeIcon('preview-file'): self.actionPreview.setIcon(QIcon.fromTheme('preview-file')) elif QIcon.hasThemeIcon('x-office-document'): self.actionPreview.setIcon(QIcon.fromTheme('x-office-document')) else: self.actionPreview.setIcon(QIcon(icon_path+'document-preview.png')) self.actionLivePreview = self.act(self.tr('Live preview'), shct=Qt.CTRL+Qt.Key_L, trigbool=self.enableLivePreview) menuPreview = QMenu() menuPreview.addAction(self.actionLivePreview) self.actionPreview.setMenu(menuPreview) self.actionTableMode = self.act(self.tr('Table editing mode'), shct=Qt.CTRL+Qt.Key_T, trigbool=lambda x: self.currentTab.editBox.enableTableMode(x)) if ReTextFakeVimHandler: self.actionFakeVimMode = self.act(self.tr('FakeVim mode'), shct=Qt.CTRL+Qt.ALT+Qt.Key_V, trigbool=self.enableFakeVimMode) if globalSettings.useFakeVim: self.actionFakeVimMode.setChecked(True) self.enableFakeVimMode(True) self.actionFullScreen = self.act(self.tr('Fullscreen mode'), 'view-fullscreen', shct=Qt.Key_F11, trigbool=self.enableFullScreen) self.actionFullScreen.setPriority(QAction.LowPriority) self.actionConfig = self.act(self.tr('Preferences'), icon='preferences-system', trig=self.openConfigDialog) self.actionConfig.setMenuRole(QAction.PreferencesRole) self.actionSaveHtml = self.act('HTML', 'text-html', self.saveFileHtml) self.actionPdf = self.act('PDF', 'application-pdf', self.savePdf) self.actionOdf = self.act('ODT', 'x-office-document', self.saveOdf) self.getExportExtensionsList() self.actionQuit = self.act(self.tr('Quit'), 'application-exit', shct=QKeySequence.Quit) self.actionQuit.setMenuRole(QAction.QuitRole) self.actionQuit.triggered.connect(self.close) self.actionUndo = self.act(self.tr('Undo'), 'edit-undo', lambda: self.currentTab.editBox.undo(), shct=QKeySequence.Undo) self.actionRedo = self.act(self.tr('Redo'), 'edit-redo', lambda: self.currentTab.editBox.redo(), shct=QKeySequence.Redo) self.actionCopy = self.act(self.tr('Copy'), 'edit-copy', lambda: self.currentTab.editBox.copy(), shct=QKeySequence.Copy) self.actionCut = self.act(self.tr('Cut'), 'edit-cut', lambda: self.currentTab.editBox.cut(), shct=QKeySequence.Cut) self.actionPaste = self.act(self.tr('Paste'), 'edit-paste', lambda: self.currentTab.editBox.paste(), shct=QKeySequence.Paste) self.actionUndo.setEnabled(False) self.actionRedo.setEnabled(False) self.actionCopy.setEnabled(False) self.actionCut.setEnabled(False) qApp = QApplication.instance() qApp.clipboard().dataChanged.connect(self.clipboardDataChanged) self.clipboardDataChanged() if enchant is not None: self.actionEnableSC = self.act(self.tr('Enable'), trigbool=self.enableSpellCheck) self.actionSetLocale = self.act(self.tr('Set locale'), trig=self.changeLocale) self.actionWebKit = self.act(self.tr('Use WebKit renderer'), trigbool=self.enableWebKit) if ReTextWebPreview is None: globalSettings.useWebKit = False self.actionWebKit.setEnabled(False) self.actionWebKit.setChecked(globalSettings.useWebKit) self.actionShow = self.act(self.tr('Show directory'), 'system-file-manager', self.showInDir) self.actionFind = self.act(self.tr('Next'), 'go-next', self.find, shct=QKeySequence.FindNext) self.actionFindPrev = self.act(self.tr('Previous'), 'go-previous', lambda: self.find(back=True), shct=QKeySequence.FindPrevious) self.actionReplace = self.act(self.tr('Replace'), 'edit-find-replace', lambda: self.find(replace=True)) self.actionReplaceAll = self.act(self.tr('Replace all'), trig=self.replaceAll) menuReplace = QMenu() menuReplace.addAction(self.actionReplaceAll) self.actionReplace.setMenu(menuReplace) self.actionCloseSearch = self.act(self.tr('Close'), 'window-close', lambda: self.searchBar.setVisible(False)) self.actionCloseSearch.setPriority(QAction.LowPriority) self.actionHelp = self.act(self.tr('Get help online'), 'help-contents', self.openHelp) self.aboutWindowTitle = self.tr('About ReText') self.actionAbout = self.act(self.aboutWindowTitle, 'help-about', self.aboutDialog) self.actionAbout.setMenuRole(QAction.AboutRole) self.actionAboutQt = self.act(self.tr('About Qt')) self.actionAboutQt.setMenuRole(QAction.AboutQtRole) self.actionAboutQt.triggered.connect(qApp.aboutQt) availableMarkups = markups.get_available_markups() if not availableMarkups: print('Warning: no markups are available!') if len(availableMarkups) > 1: self.chooseGroup = QActionGroup(self) markupActions = [] for markup in availableMarkups: markupAction = self.act(markup.name, trigbool=self.markupFunction(markup)) if markup.name == globalSettings.defaultMarkup: markupAction.setChecked(True) self.chooseGroup.addAction(markupAction) markupActions.append(markupAction) self.actionBold = self.act(self.tr('Bold'), shct=QKeySequence.Bold, trig=lambda: self.insertFormatting('bold')) self.actionItalic = self.act(self.tr('Italic'), shct=QKeySequence.Italic, trig=lambda: self.insertFormatting('italic')) self.actionUnderline = self.act(self.tr('Underline'), shct=QKeySequence.Underline, trig=lambda: self.insertFormatting('underline')) self.usefulTags = ('header', 'italic', 'bold', 'underline', 'numbering', 'bullets', 'image', 'link', 'inline code', 'code block', 'blockquote') self.usefulChars = ('deg', 'divide', 'dollar', 'hellip', 'laquo', 'larr', 'lsquo', 'mdash', 'middot', 'minus', 'nbsp', 'ndash', 'raquo', 'rarr', 'rsquo', 'times') self.formattingBox = QComboBox(self.editBar) self.formattingBox.addItem(self.tr('Formatting')) self.formattingBox.addItems(self.usefulTags) self.formattingBox.activated[str].connect(self.insertFormatting) self.symbolBox = QComboBox(self.editBar) self.symbolBox.addItem(self.tr('Symbols')) self.symbolBox.addItems(self.usefulChars) self.symbolBox.activated.connect(self.insertSymbol) self.updateStyleSheet() menubar = self.menuBar() menuFile = menubar.addMenu(self.tr('File')) menuEdit = menubar.addMenu(self.tr('Edit')) menuHelp = menubar.addMenu(self.tr('Help')) menuFile.addAction(self.actionNew) menuFile.addAction(self.actionOpen) self.menuRecentFiles = menuFile.addMenu(self.tr('Open recent')) self.menuRecentFiles.aboutToShow.connect(self.updateRecentFiles) menuFile.addAction(self.actionShow) menuFile.addAction(self.actionSetEncoding) menuFile.addAction(self.actionReload) menuFile.addSeparator() menuFile.addAction(self.actionSave) menuFile.addAction(self.actionSaveAs) menuFile.addSeparator() menuFile.addAction(self.actionNextTab) menuFile.addAction(self.actionPrevTab) menuFile.addSeparator() menuExport = menuFile.addMenu(self.tr('Export')) menuExport.addAction(self.actionSaveHtml) menuExport.addAction(self.actionOdf) menuExport.addAction(self.actionPdf) if self.extensionActions: menuExport.addSeparator() for action, mimetype in self.extensionActions: menuExport.addAction(action) menuExport.aboutToShow.connect(self.updateExtensionsVisibility) menuFile.addAction(self.actionPrint) menuFile.addAction(self.actionPrintPreview) menuFile.addSeparator() menuFile.addAction(self.actionQuit) menuEdit.addAction(self.actionUndo) menuEdit.addAction(self.actionRedo) menuEdit.addSeparator() menuEdit.addAction(self.actionCut) menuEdit.addAction(self.actionCopy) menuEdit.addAction(self.actionPaste) menuEdit.addSeparator() if enchant is not None: menuSC = menuEdit.addMenu(self.tr('Spell check')) menuSC.addAction(self.actionEnableSC) menuSC.addAction(self.actionSetLocale) menuEdit.addAction(self.actionSearch) menuEdit.addAction(self.actionChangeEditorFont) menuEdit.addAction(self.actionChangePreviewFont) menuEdit.addSeparator() if len(availableMarkups) > 1: self.menuMode = menuEdit.addMenu(self.tr('Default markup')) for markupAction in markupActions: self.menuMode.addAction(markupAction) menuFormat = menuEdit.addMenu(self.tr('Formatting')) menuFormat.addAction(self.actionBold) menuFormat.addAction(self.actionItalic) menuFormat.addAction(self.actionUnderline) menuEdit.addAction(self.actionWebKit) menuEdit.addSeparator() menuEdit.addAction(self.actionViewHtml) menuEdit.addAction(self.actionPreview) menuEdit.addAction(self.actionTableMode) if ReTextFakeVimHandler: menuEdit.addAction(self.actionFakeVimMode) menuEdit.addSeparator() menuEdit.addAction(self.actionFullScreen) menuEdit.addAction(self.actionConfig) menuHelp.addAction(self.actionHelp) menuHelp.addSeparator() menuHelp.addAction(self.actionAbout) menuHelp.addAction(self.actionAboutQt) toolBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) toolBar.addAction(self.actionNew) toolBar.addSeparator() toolBar.addAction(self.actionOpen) toolBar.addAction(self.actionSave) toolBar.addAction(self.actionPrint) toolBar.addSeparator() toolBar.addAction(self.actionPreview) toolBar.addAction(self.actionFullScreen) self.editBar.addAction(self.actionUndo) self.editBar.addAction(self.actionRedo) self.editBar.addSeparator() self.editBar.addAction(self.actionCut) self.editBar.addAction(self.actionCopy) self.editBar.addAction(self.actionPaste) self.editBar.addSeparator() self.editBar.addWidget(self.formattingBox) self.editBar.addWidget(self.symbolBox) self.searchEdit = QLineEdit(self.searchBar) self.searchEdit.setPlaceholderText(self.tr('Search')) self.searchEdit.returnPressed.connect(self.find) self.replaceEdit = QLineEdit(self.searchBar) self.replaceEdit.setPlaceholderText(self.tr('Replace with')) self.replaceEdit.returnPressed.connect(self.find) self.csBox = QCheckBox(self.tr('Case sensitively'), self.searchBar) self.searchBar.addWidget(self.searchEdit) self.searchBar.addWidget(self.replaceEdit) self.searchBar.addSeparator() self.searchBar.addWidget(self.csBox) self.searchBar.addAction(self.actionFindPrev) self.searchBar.addAction(self.actionFind) self.searchBar.addAction(self.actionReplace) self.searchBar.addAction(self.actionCloseSearch) self.searchBar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.searchBar.setVisible(False) self.autoSaveEnabled = globalSettings.autoSave if self.autoSaveEnabled: timer = QTimer(self) timer.start(60000) timer.timeout.connect(self.saveAll) self.ind = None if enchant is not None: self.sl = globalSettings.spellCheckLocale try: enchant.Dict(self.sl or None) except enchant.errors.Error as e: print(e, file=sys.stderr) globalSettings.spellCheck = False if globalSettings.spellCheck: self.actionEnableSC.setChecked(True) self.fileSystemWatcher = QFileSystemWatcher() self.fileSystemWatcher.fileChanged.connect(self.fileChanged) def restoreLastOpenedFiles(self): for file in readListFromSettings("lastFileList"): self.openFileWrapper(file) # Show the tab of last opened file lastTabIndex = globalSettings.lastTabIndex if lastTabIndex >= 0 and lastTabIndex < self.tabWidget.count(): self.tabWidget.setCurrentIndex(lastTabIndex) def iterateTabs(self): for i in range(self.tabWidget.count()): yield self.tabWidget.widget(i) def updateStyleSheet(self): if globalSettings.styleSheet: sheetfile = QFile(globalSettings.styleSheet) sheetfile.open(QIODevice.ReadOnly) self.ss = QTextStream(sheetfile).readAll() sheetfile.close() else: palette = QApplication.palette() self.ss = 'html { color: %s; }\n' % palette.color(QPalette.WindowText).name() self.ss += 'td, th { border: 1px solid #c3c3c3; padding: 0 3px 0 3px; }\n' self.ss += 'table { border-collapse: collapse; }\n' def initTabWidget(self): def dragEnterEvent(e): e.acceptProposedAction() def dropEvent(e): fn = bytes(e.mimeData().data('text/plain')).decode().rstrip() if fn.startswith('file:'): fn = QUrl(fn).toLocalFile() self.openFileWrapper(fn) self.tabWidget.setTabsClosable(True) self.tabWidget.setAcceptDrops(True) self.tabWidget.setMovable(True) self.tabWidget.dragEnterEvent = dragEnterEvent self.tabWidget.dropEvent = dropEvent if hasattr(self.tabWidget, 'setTabBarAutoHide'): self.tabWidget.setTabBarAutoHide(globalSettings.tabBarAutoHide) def act(self, name, icon=None, trig=None, trigbool=None, shct=None): if not isinstance(shct, QKeySequence): shct = QKeySequence(shct) if icon: action = QAction(self.actIcon(icon), name, self) else: action = QAction(name, self) if trig: action.triggered.connect(trig) elif trigbool: action.setCheckable(True) action.triggered[bool].connect(trigbool) if shct: action.setShortcut(shct) return action def actIcon(self, name): return QIcon.fromTheme(name, QIcon(icon_path+name+'.png')) def printError(self): import traceback print('Exception occurred while parsing document:', file=sys.stderr) traceback.print_exc() def tabFileNameChanged(self, tab): ''' Perform all UI state changes that need to be done when the filename of the current tab has changed. ''' if tab == self.currentTab: if tab.fileName: self.setWindowTitle("") self.setWindowFilePath(tab.fileName) self.tabWidget.setTabText(self.ind, tab.getBaseName()) self.tabWidget.setTabToolTip(self.ind, tab.fileName) QDir.setCurrent(QFileInfo(tab.fileName).dir().path()) else: self.setWindowFilePath('') self.setWindowTitle(self.tr('New document') + '[*]') canReload = bool(tab.fileName) and not self.autoSaveActive(tab) self.actionSetEncoding.setEnabled(canReload) self.actionReload.setEnabled(canReload) def tabActiveMarkupChanged(self, tab): ''' Perform all UI state changes that need to be done when the active markup class of the current tab has changed. ''' if tab == self.currentTab: markupClass = tab.getActiveMarkupClass() dtMarkdown = (markupClass == markups.MarkdownMarkup) dtMkdOrReST = dtMarkdown or (markupClass == markups.ReStructuredTextMarkup) self.formattingBox.setEnabled(dtMarkdown) self.symbolBox.setEnabled(dtMarkdown) self.actionUnderline.setEnabled(dtMarkdown) self.actionBold.setEnabled(dtMkdOrReST) self.actionItalic.setEnabled(dtMkdOrReST) def tabModificationStateChanged(self, tab): ''' Perform all UI state changes that need to be done when the modification state of the current tab has changed. ''' if tab == self.currentTab: changed = tab.editBox.document().isModified() if self.autoSaveActive(tab): changed = False self.actionSave.setEnabled(changed) self.setWindowModified(changed) def createTab(self, fileName): self.currentTab = ReTextTab(self, fileName, previewState=int(globalSettings.livePreviewByDefault)) self.currentTab.fileNameChanged.connect(lambda: self.tabFileNameChanged(self.currentTab)) self.currentTab.modificationStateChanged.connect(lambda: self.tabModificationStateChanged(self.currentTab)) self.currentTab.activeMarkupChanged.connect(lambda: self.tabActiveMarkupChanged(self.currentTab)) self.tabWidget.addTab(self.currentTab, self.tr("New document")) self.currentTab.updateBoxesVisibility() def closeTab(self, ind): if self.maybeSave(ind): if self.tabWidget.count() == 1: self.createTab("") closedTab = self.tabWidget.widget(ind) if closedTab.fileName: self.fileSystemWatcher.removePath(closedTab.fileName) self.tabWidget.removeTab(ind) closedTab.deleteLater() def changeIndex(self, ind): ''' This function is called when a different tab is selected. It changes the state of the window to mirror the current state of the newly selected tab. Future changes to this state will be done in response to signals emitted by the tab, to which the window was subscribed when the tab was created. The window is subscribed to all tabs like this, but only the active tab will logically generate these signals. Aside from the above this function also calls the handlers for the other changes that are implied by a tab switch: filename change, modification state change and active markup change. ''' self.currentTab = self.tabWidget.currentWidget() editBox = self.currentTab.editBox previewState = self.currentTab.previewState self.actionUndo.setEnabled(editBox.document().isUndoAvailable()) self.actionRedo.setEnabled(editBox.document().isRedoAvailable()) self.actionCopy.setEnabled(editBox.textCursor().hasSelection()) self.actionCut.setEnabled(editBox.textCursor().hasSelection()) self.actionPreview.setChecked(previewState >= PreviewLive) self.actionLivePreview.setChecked(previewState == PreviewLive) self.actionTableMode.setChecked(editBox.tableModeEnabled) self.editBar.setEnabled(previewState < PreviewNormal) self.ind = ind editBox.setFocus(Qt.OtherFocusReason) self.tabFileNameChanged(self.currentTab) self.tabModificationStateChanged(self.currentTab) self.tabActiveMarkupChanged(self.currentTab) def changeEditorFont(self): font, ok = QFontDialog.getFont(globalSettings.editorFont, self) if ok: globalSettings.editorFont = font for tab in self.iterateTabs(): tab.editBox.updateFont() def changePreviewFont(self): font, ok = QFontDialog.getFont(globalSettings.font, self) if ok: globalSettings.font = font for tab in self.iterateTabs(): tab.triggerPreviewUpdate() def preview(self, viewmode): self.currentTab.previewState = viewmode * 2 self.actionLivePreview.setChecked(False) self.editBar.setDisabled(viewmode) self.currentTab.updateBoxesVisibility() def enableLivePreview(self, livemode): self.currentTab.previewState = int(livemode) self.actionPreview.setChecked(livemode) self.editBar.setEnabled(True) self.currentTab.updateBoxesVisibility() def enableWebKit(self, enable): globalSettings.useWebKit = enable for tab in self.iterateTabs(): tab.previewBox.disconnectExternalSignals() tab.previewBox.setParent(None) tab.previewBox.deleteLater() tab.previewBox = tab.createPreviewBox(tab.editBox) tab.previewBox.setMinimumWidth(125) tab.addWidget(tab.previewBox) tab.setSizes((50, 50)) tab.triggerPreviewUpdate() tab.updateBoxesVisibility() def enableCopy(self, copymode): self.actionCopy.setEnabled(copymode) self.actionCut.setEnabled(copymode) def enableFullScreen(self, yes): if yes: self.showFullScreen() else: self.showNormal() def openConfigDialog(self): dlg = ConfigDialog(self) dlg.setWindowTitle(self.tr('Preferences')) dlg.show() def enableFakeVimMode(self, yes): globalSettings.useFakeVim = yes if yes: FakeVimMode.init(self) for tab in self.iterateTabs(): tab.editBox.installFakeVimHandler() else: FakeVimMode.exit(self) def enableSpellCheck(self, yes): try: dict = enchant.Dict(self.sl or None) except enchant.errors.Error as e: QMessageBox.warning(self, '', str(e)) self.actionEnableSC.setChecked(False) yes = False self.setAllDictionaries(dict if yes else None) globalSettings.spellCheck = yes def setAllDictionaries(self, dictionary): for tab in self.iterateTabs(): hl = tab.highlighter hl.dictionary = dictionary hl.rehighlight() def changeLocale(self): localedlg = LocaleDialog(self, defaultText=self.sl) if localedlg.exec() != QDialog.Accepted: return sl = localedlg.localeEdit.text() try: enchant.Dict(sl or None) except enchant.errors.Error as e: QMessageBox.warning(self, '', str(e)) else: self.sl = sl or None self.enableSpellCheck(self.actionEnableSC.isChecked()) if localedlg.checkBox.isChecked(): globalSettings.spellCheckLocale = sl def searchBarVisibilityChanged(self, visible): self.actionSearch.setChecked(visible) if visible: self.searchEdit.setFocus(Qt.ShortcutFocusReason) def find(self, back=False, replace=False): flags = QTextDocument.FindFlags() if back: flags |= QTextDocument.FindBackward if self.csBox.isChecked(): flags |= QTextDocument.FindCaseSensitively text = self.searchEdit.text() replaceText = self.replaceEdit.text() if replace else None found = self.currentTab.find(text, flags, replaceText=replaceText) self.setSearchEditColor(found) def replaceAll(self): text = self.searchEdit.text() replaceText = self.replaceEdit.text() found = self.currentTab.replaceAll(text, replaceText) self.setSearchEditColor(found) def setSearchEditColor(self, found): palette = self.searchEdit.palette() palette.setColor(QPalette.Active, QPalette.Base, Qt.white if found else QColor(255, 102, 102)) self.searchEdit.setPalette(palette) def showInDir(self): if self.currentTab.fileName: path = QFileInfo(self.currentTab.fileName).path() QDesktopServices.openUrl(QUrl.fromLocalFile(path)) else: QMessageBox.warning(self, '', self.tr("Please, save the file somewhere.")) def moveToTopOfRecentFileList(self, fileName): if fileName: files = readListFromSettings("recentFileList") if fileName in files: files.remove(fileName) files.insert(0, fileName) if len(files) > 10: del files[10:] writeListToSettings("recentFileList", files) def createNew(self, text=None): self.createTab("") self.ind = self.tabWidget.count()-1 self.tabWidget.setCurrentIndex(self.ind) if text: self.currentTab.editBox.textCursor().insertText(text) def switchTab(self, shift=1): self.tabWidget.setCurrentIndex((self.ind + shift) % self.tabWidget.count()) def updateRecentFiles(self): self.menuRecentFiles.clear() self.recentFilesActions = [] filesOld = readListFromSettings("recentFileList") files = [] for f in filesOld: if QFile.exists(f): files.append(f) self.recentFilesActions.append(self.act(f, trig=self.openFunction(f))) writeListToSettings("recentFileList", files) for action in self.recentFilesActions: self.menuRecentFiles.addAction(action) def markupFunction(self, markup): return lambda: self.setDefaultMarkup(markup) def openFunction(self, fileName): return lambda: self.openFileWrapper(fileName) def extensionFunction(self, data): return lambda: \ self.runExtensionCommand(data['Exec'], data['FileFilter'], data['DefaultExtension']) def getExportExtensionsList(self): extensions = [] for extsprefix in datadirs: extsdir = QDir(extsprefix+'/export-extensions/') if extsdir.exists(): for fileInfo in extsdir.entryInfoList(['*.desktop', '*.ini'], QDir.Files | QDir.Readable): extensions.append(self.readExtension(fileInfo.filePath())) locale = QLocale.system().name() self.extensionActions = [] for extension in extensions: try: if ('Name[%s]' % locale) in extension: name = extension['Name[%s]' % locale] elif ('Name[%s]' % locale.split('_')[0]) in extension: name = extension['Name[%s]' % locale.split('_')[0]] else: name = extension['Name'] data = {} for prop in ('FileFilter', 'DefaultExtension', 'Exec'): if 'X-ReText-'+prop in extension: data[prop] = extension['X-ReText-'+prop] elif prop in extension: data[prop] = extension[prop] else: data[prop] = '' action = self.act(name, trig=self.extensionFunction(data)) if 'Icon' in extension: action.setIcon(self.actIcon(extension['Icon'])) mimetype = extension['MimeType'] if 'MimeType' in extension else None except KeyError: print('Failed to parse extension: Name is required', file=sys.stderr) else: self.extensionActions.append((action, mimetype)) def updateExtensionsVisibility(self): markupClass = self.currentTab.getActiveMarkupClass() for action in self.extensionActions: if markupClass is None: action[0].setEnabled(False) continue mimetype = action[1] if mimetype is None: enabled = True elif markupClass == markups.MarkdownMarkup: enabled = (mimetype in ("text/x-retext-markdown", "text/x-markdown", "text/markdown")) elif markupClass == markups.ReStructuredTextMarkup: enabled = (mimetype in ("text/x-retext-rst", "text/x-rst")) else: enabled = False action[0].setEnabled(enabled) def readExtension(self, fileName): extFile = QFile(fileName) extFile.open(QIODevice.ReadOnly) extension = {} stream = QTextStream(extFile) while not stream.atEnd(): line = stream.readLine() if '=' in line: index = line.index('=') extension[line[:index].rstrip()] = line[index+1:].lstrip() extFile.close() return extension def openFile(self): supportedExtensions = ['.txt'] for markup in markups.get_all_markups(): supportedExtensions += markup.file_extensions fileFilter = ' (' + str.join(' ', ['*'+ext for ext in supportedExtensions]) + ');;' fileNames = QFileDialog.getOpenFileNames(self, self.tr("Select one or several files to open"), "", self.tr("Supported files") + fileFilter + self.tr("All files (*)")) for fileName in fileNames[0]: self.openFileWrapper(fileName) def openFileWrapper(self, fileName): if not fileName: return fileName = QFileInfo(fileName).canonicalFilePath() exists = False for i, tab in enumerate(self.iterateTabs()): if tab.fileName == fileName: exists = True ex = i if exists: self.tabWidget.setCurrentIndex(ex) elif QFile.exists(fileName): noEmptyTab = ( (self.ind is None) or self.currentTab.fileName or self.currentTab.editBox.toPlainText() or self.currentTab.editBox.document().isModified() ) if noEmptyTab: self.createTab(fileName) self.ind = self.tabWidget.count()-1 self.tabWidget.setCurrentIndex(self.ind) if fileName: self.fileSystemWatcher.addPath(fileName) self.currentTab.readTextFromFile(fileName) self.moveToTopOfRecentFileList(self.currentTab.fileName) def showEncodingDialog(self): if not self.maybeSave(self.ind): return codecsSet = set(bytes(QTextCodec.codecForName(alias).name()) for alias in QTextCodec.availableCodecs()) encoding, ok = QInputDialog.getItem(self, '', self.tr('Select file encoding from the list:'), [bytes(b).decode() for b in sorted(codecsSet)], 0, False) if ok: self.currentTab.readTextFromFile(None, encoding) def saveFileAs(self): self.saveFile(dlg=True) def saveAll(self): for tab in self.iterateTabs(): if tab.fileName and QFileInfo(tab.fileName).isWritable(): tab.saveTextToFile() def saveFile(self, dlg=False): fileNameToSave = self.currentTab.fileName if (not fileNameToSave) or dlg: markupClass = self.currentTab.getActiveMarkupClass() if (markupClass is None) or not hasattr(markupClass, 'default_extension'): defaultExt = self.tr("Plain text (*.txt)") ext = ".txt" else: defaultExt = self.tr('%s files', 'Example of final string: Markdown files') \ % markupClass.name + ' (' + str.join(' ', ('*'+extension for extension in markupClass.file_extensions)) + ')' if markupClass == markups.MarkdownMarkup: ext = globalSettings.markdownDefaultFileExtension elif markupClass == markups.ReStructuredTextMarkup: ext = globalSettings.restDefaultFileExtension else: ext = markupClass.default_extension fileNameToSave = QFileDialog.getSaveFileName(self, self.tr("Save file"), "", defaultExt)[0] if fileNameToSave: if not QFileInfo(fileNameToSave).suffix(): fileNameToSave += ext # Make sure we don't overwrite a file opened in other tab for tab in self.iterateTabs(): if tab is not self.currentTab and tab.fileName == fileNameToSave: QMessageBox.warning(self, "", self.tr("Cannot save to file which is open in another tab!")) return False self.actionSetEncoding.setDisabled(self.autoSaveActive()) if fileNameToSave: if self.currentTab.saveTextToFile(fileNameToSave): self.moveToTopOfRecentFileList(self.currentTab.fileName) return True else: QMessageBox.warning(self, '', self.tr("Cannot save to file because it is read-only!")) return False def saveHtml(self, fileName): if not QFileInfo(fileName).suffix(): fileName += ".html" try: _, htmltext, _ = self.currentTab.getDocumentForExport(includeStyleSheet=False, webenv=True) except Exception: return self.printError() htmlFile = QFile(fileName) htmlFile.open(QIODevice.WriteOnly) html = QTextStream(htmlFile) if globalSettings.defaultCodec: html.setCodec(globalSettings.defaultCodec) html << htmltext htmlFile.close() def textDocument(self, title, htmltext): td = QTextDocument() td.setMetaInformation(QTextDocument.DocumentTitle, title) if self.ss: td.setDefaultStyleSheet(self.ss) td.setHtml(htmltext) td.setDefaultFont(globalSettings.font) return td def saveOdf(self): title, htmltext, _ = self.currentTab.getDocumentForExport(includeStyleSheet=True, webenv=False) try: document = self.textDocument(title, htmltext) except Exception: return self.printError() fileName = QFileDialog.getSaveFileName(self, self.tr("Export document to ODT"), "", self.tr("OpenDocument text files (*.odt)"))[0] if not QFileInfo(fileName).suffix(): fileName += ".odt" writer = QTextDocumentWriter(fileName) writer.setFormat(b"odf") writer.write(document) def saveFileHtml(self): fileName = QFileDialog.getSaveFileName(self, self.tr("Save file"), "", self.tr("HTML files (*.html *.htm)"))[0] if fileName: self.saveHtml(fileName) def getDocumentForPrint(self, title, htmltext, preview): if globalSettings.useWebKit: return preview try: return self.textDocument(title, htmltext) except Exception: self.printError() def standardPrinter(self, title): printer = QPrinter(QPrinter.HighResolution) printer.setDocName(title) printer.setCreator('ReText %s' % app_version) return printer def savePdf(self): fileName = QFileDialog.getSaveFileName(self, self.tr("Export document to PDF"), "", self.tr("PDF files (*.pdf)"))[0] if fileName: if not QFileInfo(fileName).suffix(): fileName += ".pdf" title, htmltext, preview = self.currentTab.getDocumentForExport(includeStyleSheet=True, webenv=False) printer = self.standardPrinter(title) printer.setOutputFormat(QPrinter.PdfFormat) printer.setOutputFileName(fileName) document = self.getDocumentForPrint(title, htmltext, preview) if document != None: document.print(printer) def printFile(self): title, htmltext, preview = self.currentTab.getDocumentForExport(includeStyleSheet=True, webenv=False) printer = self.standardPrinter(title) dlg = QPrintDialog(printer, self) dlg.setWindowTitle(self.tr("Print document")) if (dlg.exec() == QDialog.Accepted): document = self.getDocumentForPrint(title, htmltext, preview) if document != None: document.print(printer) def printPreview(self): title, htmltext, preview = self.currentTab.getDocumentForExport(includeStyleSheet=True, webenv=False) document = self.getDocumentForPrint(title, htmltext, preview) if document is None: return printer = self.standardPrinter(title) preview = QPrintPreviewDialog(printer, self) preview.paintRequested.connect(document.print) preview.exec() def runExtensionCommand(self, command, filefilter, defaultext): import shlex of = ('%of' in command) html = ('%html' in command) if of: if defaultext and not filefilter: filefilter = '*'+defaultext fileName = QFileDialog.getSaveFileName(self, self.tr('Export document'), '', filefilter)[0] if not fileName: return if defaultext and not QFileInfo(fileName).suffix(): fileName += defaultext else: fileName = 'out' + defaultext basename = '.%s.retext-temp' % self.currentTab.getBaseName() if html: tmpname = basename+'.html' self.saveHtml(tmpname) else: tmpname = basename + self.currentTab.getActiveMarkupClass().default_extension self.currentTab.writeTextToFile(tmpname) command = command.replace('%of', shlex.quote(fileName)) command = command.replace('%html' if html else '%if', shlex.quote(tmpname)) try: Popen(str(command), shell=True).wait() except Exception as error: errorstr = str(error) QMessageBox.warning(self, '', self.tr('Failed to execute the command:') + '\n' + errorstr) QFile(tmpname).remove() def autoSaveActive(self, tab=None): tab = tab if tab else self.currentTab return bool(self.autoSaveEnabled and tab.fileName and QFileInfo(tab.fileName).isWritable()) def clipboardDataChanged(self): mimeData = QApplication.instance().clipboard().mimeData() if mimeData is not None: self.actionPaste.setEnabled(mimeData.hasText() or mimeData.hasImage()) def insertFormatting(self, formatting): cursor = self.currentTab.editBox.textCursor() text = cursor.selectedText() moveCursorTo = None def c(cursor): nonlocal moveCursorTo moveCursorTo = cursor.position() def ensurenl(cursor): if not cursor.atBlockStart(): cursor.insertText('\n\n') toinsert = { 'header': (ensurenl, '# ', text), 'italic': ('*', text, c, '*'), 'bold': ('**', text, c, '**'), 'underline': ('<u>', text, c, '</u>'), 'numbering': (ensurenl, ' 1. ', text), 'bullets': (ensurenl, ' * ', text), 'image': ('![', text or self.tr('Alt text'), c, '](', self.tr('URL'), ')'), 'link': ('[', text or self.tr('Link text'), c, '](', self.tr('URL'), ')'), 'inline code': ('`', text, c, '`'), 'code block': (ensurenl, ' ', text), 'blockquote': (ensurenl, '> ', text), } if formatting not in toinsert: return cursor.beginEditBlock() for token in toinsert[formatting]: if callable(token): token(cursor) else: cursor.insertText(token) cursor.endEditBlock() self.formattingBox.setCurrentIndex(0) # Bring back the focus on the editor self.currentTab.editBox.setFocus(Qt.OtherFocusReason) if moveCursorTo: cursor.setPosition(moveCursorTo) self.currentTab.editBox.setTextCursor(cursor) def insertSymbol(self, num): if num: self.currentTab.editBox.insertPlainText('&'+self.usefulChars[num-1]+';') self.symbolBox.setCurrentIndex(0) def fileChanged(self, fileName): ind = None for testind, tab in enumerate(self.iterateTabs()): if tab.fileName == fileName: ind = testind if ind is None: self.fileSystemWatcher.removePath(fileName) self.tabWidget.setCurrentIndex(ind) if not QFile.exists(fileName): self.currentTab.editBox.document().setModified(True) QMessageBox.warning(self, '', self.tr( 'This file has been deleted by other application.\n' 'Please make sure you save the file before exit.')) elif not self.currentTab.editBox.document().isModified(): # File was not modified in ReText, reload silently self.currentTab.readTextFromFile() else: text = self.tr( 'This document has been modified by other application.\n' 'Do you want to reload the file (this will discard all ' 'your changes)?\n') if self.autoSaveEnabled: text += self.tr( 'If you choose to not reload the file, auto save mode will ' 'be disabled for this session to prevent data loss.') messageBox = QMessageBox(QMessageBox.Warning, '', text) reloadButton = messageBox.addButton(self.tr('Reload'), QMessageBox.YesRole) messageBox.addButton(QMessageBox.Cancel) messageBox.exec() if messageBox.clickedButton() is reloadButton: self.currentTab.readTextFromFile() else: self.autoSaveEnabled = False self.currentTab.editBox.document().setModified(True) if fileName not in self.fileSystemWatcher.files(): # https://github.com/retext-project/retext/issues/137 self.fileSystemWatcher.addPath(fileName) def maybeSave(self, ind): tab = self.tabWidget.widget(ind) if self.autoSaveActive(tab): tab.saveTextToFile() return True if not tab.editBox.document().isModified(): return True self.tabWidget.setCurrentIndex(ind) ret = QMessageBox.warning(self, '', self.tr("The document has been modified.\nDo you want to save your changes?"), QMessageBox.Save | QMessageBox.Discard | QMessageBox.Cancel) if ret == QMessageBox.Save: return self.saveFile(False) elif ret == QMessageBox.Cancel: return False return True def closeEvent(self, closeevent): for ind in range(self.tabWidget.count()): if not self.maybeSave(ind): return closeevent.ignore() if globalSettings.saveWindowGeometry and not self.isMaximized(): globalSettings.windowGeometry = self.saveGeometry() if globalSettings.openLastFilesOnStartup: files = [tab.fileName for tab in self.iterateTabs()] writeListToSettings("lastFileList", files) globalSettings.lastTabIndex = self.tabWidget.currentIndex() closeevent.accept() def viewHtml(self): htmlDlg = HtmlDialog(self) try: _, htmltext, _ = self.currentTab.getDocumentForExport(includeStyleSheet=False, webenv=False) except Exception: return self.printError() winTitle = self.currentTab.getBaseName() htmlDlg.setWindowTitle(winTitle+" ("+self.tr("HTML code")+")") htmlDlg.textEdit.setPlainText(htmltext.rstrip()) htmlDlg.hl.rehighlight() htmlDlg.show() htmlDlg.raise_() htmlDlg.activateWindow() def openHelp(self): QDesktopServices.openUrl(QUrl('https://github.com/retext-project/retext/wiki')) def aboutDialog(self): QMessageBox.about(self, self.aboutWindowTitle, '<p><b>' + (self.tr('ReText %s (using PyMarkups %s)') % (app_version, markups.__version__)) +'</b></p>' + self.tr('Simple but powerful editor' ' for Markdown and reStructuredText') +'</p><p>'+self.tr('Author: Dmitry Shachnev, 2011').replace('2011', '2011–2016') +'<br><a href="https://github.com/retext-project/retext">'+self.tr('Website') +'</a> | <a href="http://daringfireball.net/projects/markdown/syntax">' +self.tr('Markdown syntax') +'</a> | <a href="http://docutils.sourceforge.net/docs/user/rst/quickref.html">' +self.tr('reStructuredText syntax')+'</a></p>') def setDefaultMarkup(self, markupClass): globalSettings.defaultMarkup = markupClass.name for tab in self.iterateTabs(): if not tab.fileName: tab.updateActiveMarkupClass()
class MainWindow(TestableMainWindow): # ------------------------------------------------------------------------- def __init__(self, test_mode=False): super(MainWindow, self).__init__(None) self.setWindowIcon(QIcon(img('editor/editor.png'))) self.cfg = Config() self._tabs = QTabWidget() self._actions: Dict[str, QAction] = {} self._menus: Dict[str, QMenu] = {} self._make_actions() self._spell = SpellChecker(enabled=not test_mode) self._init_ui() # ------------------------------------------------------------------------- def _actions_data(self): about_qt = QApplication.instance().aboutQt for name, title, triggered in ( ("new", self.tr("New"), lambda: self.open_file()), ("open", self.tr("Open"), self.open), ("save", self.tr("Save"), self.save), ("save_as", self.tr("Save as ..."), self.saveas), ("exit", self.tr("Exit"), self.close), ("about", self.tr("About"), self.about), ("about_qt", self.tr("About Qt"), about_qt), ("help", self.tr("User manual"), lambda: self.help()), ("eng", self.tr("English"), lambda: self.set_lang("en_EN")), ("rus", self.tr("Russian"), lambda: self.set_lang("ru_RU")), ("text", self.tr("Only text"), lambda: self.set_format("txt")), ("html", self.tr("HTML"), lambda: self.set_format("html")), ("no-syntax", self.tr("--no--"), lambda: self.set_syntax("--no--")), ("python", self.tr("Python"), lambda: self.set_syntax("Python")), ("sql", self.tr("SQL"), lambda: self.set_syntax("SQL")), ("invisible-symbol", self.tr("Show/hide invisible symbol"), self.set_invisible_symbol), ("read-only", self.tr("Read only"), self._read_only), ("word-wrap", self.tr("Word wrap"), self.word_wrap), ("rus-spell", self.tr("russian language"), lambda x: self._spell.set_enabled("rus", x)), ("eng-spell", self.tr("english language"), lambda x: self._spell.set_enabled("eng", x)), ): yield name, title, triggered # ------------------------------------------------------------------------- def _read_only(self): if not self._tabs.count(): # pragma: no cover return w = self._tabs.widget(self._tabs.currentIndex()) w.set_read_only(not w.is_read_only()) self.change_tab(self._tabs.currentIndex()) # ------------------------------------------------------------------------- def _make_actions(self): for name, title, triggered in self._actions_data(): self._actions[name] = QAction(title, None) self._actions[name].setIcon(QIcon(img("editor/" + name))) if triggered: self._actions[name].triggered.connect(triggered) # ------------------------------------------------------------------------- def retranslate_ui(self): for name, title, _ in self._actions_data(): self._actions[name].setText(title) self._actions[name].setStatusTip(title) self._actions[name].setToolTip(title) self._menus["file"].setTitle(self.tr("&File")) self._menus["lang"].setTitle(self.tr("&Language")) self._menus["help"].setTitle(self.tr("&Help")) self._menus["view"].setTitle(self.tr("&View")) self._menus["format"].setTitle(self.tr("&Format")) self._menus["syntax"].setTitle(self.tr("&Syntax")) self._menus["spell"].setTitle(self.tr("&Check spelling")) idx_current = self._tabs.currentIndex() idx_help = -1 for i in range(self._tabs.count()): w = self._tabs.widget(i) w.retranslate_ui() self._tabs.setTabText(i, w.get_name()) path = (w.get_path() + "/") if not w.is_help_text() else "" self._tabs.setTabToolTip(i, f"{path}{w.get_name()}") if w.is_help_text(): idx_help = i # if need, reopen user manual with current language if idx_help != -1: self.close_tab(idx_help) self.help(position=idx_help) self._tabs.setCurrentIndex(idx_current) self.setWindowTitle(self.tr("[Demo] Embedded text editor")) # ------------------------------------------------------------------------- def _init_ui(self): self.setWindowTitle(self.tr("[Demo] Embedded text editor")) self.setGeometry(800, 150, 1000, 500) self._tabs.setTabsClosable(True) self._tabs.setMovable(True) self._tabs.tabCloseRequested.connect(self.close_tab) self._tabs.currentChanged.connect(self.change_tab) self.setCentralWidget(self._tabs) # Setup the menu self._menus["file"] = self.menuBar().addMenu(self.tr("&File")) self._menus["view"] = self.menuBar().addMenu(self.tr("&View")) self._menus["help"] = self.menuBar().addMenu(self.tr("&Help")) # Populate the Menu with Actions self._menus["help"].addAction(self._actions["help"]) self._menus["help"].addSeparator() self._menus["help"].addAction(self._actions["about"]) # self._menus["help"].addAction(self._actions["about_qt"]) self._menus["file"].addAction(self._actions["new"]) self._menus["file"].addAction(self._actions["open"]) self._menus["file"].addAction(self._actions["save"]) self._menus["file"].addAction(self._actions["save_as"]) self._menus["file"].addSeparator() self._menus["lang"] = self._menus["file"].addMenu(self.tr("&Language")) self._menus["lang"].addAction(self._actions["eng"]) self._menus["lang"].addAction(self._actions["rus"]) self._menus["file"].addSeparator() self._menus["file"].addAction(self._actions["exit"]) self._menus["format"] = self._menus["view"].addMenu(self.tr("&Format")) self._menus["format"].addAction(self._actions["text"]) self._menus["format"].addAction(self._actions["html"]) self._menus["syntax"] = self._menus["view"].addMenu(self.tr("&Syntax")) self._menus["syntax"].addAction(self._actions["no-syntax"]) self._menus["syntax"].addAction(self._actions["python"]) self._menus["syntax"].addAction(self._actions["sql"]) self._menus["spell"] = self._menus["view"].addMenu( self.tr("&Check spelling")) self._menus["spell"].addAction(self._actions["eng-spell"]) self._menus["spell"].addAction(self._actions["rus-spell"]) self._menus["view"].addSeparator() self._menus["view"].addAction(self._actions["word-wrap"]) self._menus["view"].addAction(self._actions["invisible-symbol"]) self._menus["view"].addSeparator() self._menus["view"].addAction(self._actions["read-only"]) open_files = json.loads(self.cfg.get("open_files", "{}", system=True)) for key, (fmt, highlighter) in open_files.items(): self.open_file(key, fmt=fmt, hl=highlighter) if not self._tabs.count(): # pragma: no cover self._actions["save"].setEnabled(False) self._actions["save_as"].setEnabled(False) self._actions["word-wrap"].setCheckable(True) self._actions["read-only"].setCheckable(True) self._actions["eng-spell"].setCheckable(True) self._actions["rus-spell"].setCheckable(True) self._actions["eng-spell"].setChecked(True) self._actions["rus-spell"].setChecked(True) self._actions["eng-spell"].setEnabled(self._spell.enabled("eng")) self._actions["rus-spell"].setEnabled(self._spell.enabled("rus")) self._menus["spell"].setEnabled(self._spell.enabled("all")) self.retranslate_ui() self.read_settings() self.show() if self._tabs.count() > 0: # pragma: no cover self._tabs.widget(self._tabs.currentIndex()).set_focus() else: # pragma: no cover self._menus["view"].setEnabled(False) if not self._spell.enabled(): self._menus["spell"].menuAction().setVisible(False) # ------------------------------------------------------------------------- def set_lang(self, locale): install_translators(locale) self.retranslate_ui() # ------------------------------------------------------------------------- def word_wrap(self): if self._tabs.count() > 0: self._tabs.widget(self._tabs.currentIndex()).set_word_wrap() # ------------------------------------------------------------------------- def closeEvent(self, event): self.write_settings() """ s = (self.tr("This will exit the editor.") + "\n" + self.tr("Do you want to continue ?")) if yes_no(s, self): event.accept() else: event.ignore() """ open_files = {} for i in range(self._tabs.count()): w = self._tabs.widget(i) if w.is_modified(): # pragma: no cover self.save_text_tab(i) open_files[w.file_path()] = (w.text_format(False), w.highlighter()) self.cfg["SYSTEM", "open_files"] = json.dumps(open_files) event.accept() # ------------------------------------------------------------------------- def save_text_tab(self, idx_tab): w = self._tabs.widget(idx_tab) name = w.file_path() if w.file_path() else w.get_name() t1 = self.tr("The file") t2 = self.tr("is changed") t3 = self.tr("Save it ?") if w.is_modified(): if yes_no(f"{t1} <b>{name}</b> {t2}.<br><br>{t3}", self): w.save() # ------------------------------------------------------------------------- def close_tab(self, idx_tab): self.save_text_tab(idx_tab) self._tabs.removeTab(idx_tab) if not self._tabs.count(): # pragma: no cover self._actions["save"].setEnabled(False) self._actions["save_as"].setEnabled(False) self._menus["view"].setEnabled(False) # ------------------------------------------------------------------------- def change_tab(self, idx_tab): if not self._tabs.count(): # pragma: no cover return self._menus["view"].setEnabled(True) w = self._tabs.widget(idx_tab) self._actions["save"].setEnabled(w.is_modified()) self._tabs.setTabText(idx_tab, w.get_name()) textmode = w.text_format().upper() != "HTML" self._actions["text"].setEnabled(not textmode) self._actions["html"].setEnabled(textmode) self._actions["word-wrap"].setChecked(w.get_word_wrap()) self._actions["read-only"].setChecked(w.is_read_only() or w.is_help_text()) self._actions["read-only"].setEnabled(not w.is_help_text()) self._menus["format"].setEnabled(not w.is_read_only()) self._menus["syntax"].setEnabled(textmode) self._menus["spell"].setEnabled(not textmode and self._spell.enabled("all")) # ------------------------------------------------------------------------- def change_enabled_save(self): self.change_tab(self._tabs.currentIndex()) # ------------------------------------------------------------------------- def about(self): text = self.tr("Demo for 'Embedded text editor'") author = self.tr("(C) 2019, Vladimir Rukavishnikov") QMessageBox().about(self, self.tr("Information"), f"{text}\n\n{author}") # ------------------------------------------------------------------------- def set_format(self, name_format): if self._tabs.count() > 0: idx = self._tabs.currentIndex() self._tabs.widget(idx).set_text_format(name_format) self.change_tab(idx) # ------------------------------------------------------------------------- def set_syntax(self, name_syntax): if self._tabs.count() > 0: self._tabs.widget( self._tabs.currentIndex()).set_highlighter(name_syntax) # ------------------------------------------------------------------------- def set_invisible_symbol(self): if self._tabs.count() > 0: self._tabs.widget(self._tabs.currentIndex()).set_invisible_symbol() # ------------------------------------------------------------------------- def read_settings(self): pos = QPoint(self.cfg.get("MainWindow.X", 800, True), self.cfg.get("MainWindow.Y", 150, True)) sz = QSize(self.cfg.get("MainWindow.width", 1000, True), self.cfg.get("MainWindow.height", 500, True)) self.move(pos) self.resize(sz) # ------------------------------------------------------------------------- def write_settings(self): self.cfg["SYSTEM", "MainWindow.X"] = self.pos().x() self.cfg["SYSTEM", "MainWindow.Y"] = self.pos().y() self.cfg["SYSTEM", "MainWindow.width"] = self.size().width() self.cfg["SYSTEM", "MainWindow.height"] = self.size().height() # ------------------------------------------------------------------------- def open_file(self, path=None, fmt="html", hl=""): if path is not None and not os.path.exists(path): return # after saving config file, need to reload the configuration is_config_file = path == self.cfg.get("Config/FilePath", "") func_after_save = self.cfg.load if is_config_file else None te = TabEdit(self, file_path=path, text_format=fmt, highlighter=hl, func_after_save=func_after_save, spell=self._spell) idx = self._tabs.addTab(te, te.get_name()) self._tabs.setCurrentIndex(idx) path = (te.get_path() + "/") if not te.is_help_text() else "" self._tabs.setTabToolTip(idx, f"{path}{te.get_name()}") te.enabled_save_signal.connect(self.change_enabled_save) self._actions["save_as"].setEnabled(bool(self._tabs.count())) if te.is_help_text(): self._read_only() # ------------------------------------------------------------------------- def save(self): if self._tabs.currentIndex() >= 0: self._tabs.widget(self._tabs.currentIndex()).save() self.change_tab(self._tabs.currentIndex()) # ------------------------------------------------------------------------- def saveas(self): self._tabs.widget(self._tabs.currentIndex()).save_as() self.change_tab(self._tabs.currentIndex()) # ------------------------------------------------------------------------- def open(self): """ File dialog with selections syntax and file type. """ filedialog = QFileDialog(self) filedialog.setOption(QFileDialog.DontUseNativeDialog) filedialog.setDefaultSuffix("*.*") filedialog.setDirectory( self.cfg.get("TextEditor/LastPath", ".", system=True)) layout = filedialog.layout() type_files = [ # type, syntax, is HTML, list of exts (self.tr("All files (*)"), self.tr("-- no --"), True, []), (self.tr("Python files (*.py *.pyw)"), "Python", False, ["py", "pyw"]), (self.tr("SQL scripts (*.sql)"), "SQL", False, ["sql"]), (self.tr("Text files (*.txt)"), self.tr("-- no --"), False, ["txt"]), ] filedialog.setNameFilters([t[0] for t in type_files]) syntax = QComboBox() __in_cmb = [] for t in type_files: if t[1] not in __in_cmb: __in_cmb.append(t[1]) syntax.addItem(t[1]) lbl_syntax = QLabel(self.tr("Syntax")) format_text = QCheckBox("HTML") format_text.setChecked(True) col, row = layout.columnCount(), layout.rowCount() layout.addWidget(syntax, row, col - 1) layout.addWidget(format_text, row, col - 1) layout.addWidget(lbl_syntax, row, 0) layout.addWidget(syntax, row, col - 2) layout.itemAtPosition(row, col - 1).setAlignment(Qt.AlignCenter) layout.update() layout.activate() def _change_syntax_and_type(idx): # pragma: no cover syntax.setCurrentText(type_files[idx][1]) format_text.setChecked(type_files[idx][2]) def _filter_selected(name): # pragma: no cover for i, (nm, _, _, _) in enumerate(type_files): if name == nm: _change_syntax_and_type(i) def _current_changed(name): # pragma: no cover if "." in name: ext = name.split(".")[-1].lower() for i, (_, _, _, exts) in enumerate(type_files): if ext in exts: _change_syntax_and_type(i) return _change_syntax_and_type(0) filedialog.currentChanged.connect(_current_changed) filedialog.filterSelected.connect(_filter_selected) if filedialog.exec_(): text_format = "html" if format_text.isChecked() else "text" path = filedialog.selectedFiles()[0] self.open_file(path=path, fmt=text_format, hl=syntax.currentText()) self.cfg["SYSTEM", "TextEditor/LastPath"] = os.path.dirname(path) # ------------------------------------------------------------------------- def help(self, position=-1): """ Show text description of editor """ # check if already open for i in range(self._tabs.count()): if self._tabs.widget(i).is_help_text(): self._tabs.setCurrentIndex(i) return # open file path = os.path.join(os.path.dirname(__file__), "doc") lang = "." + get_current_language() filepath = f"{path}{os.sep}demoedit{lang}.html" if not os.path.exists(filepath): filepath = f"{path}{os.sep}demoedit.html" if not os.path.exists(filepath): # pragma: no cover return self.open_file(filepath, hl="Python") if position != -1: self._tabs.tabBar().moveTab(self._tabs.count() - 1, position)
class Dialog(QDialog): def __init__(self, parent=None): super(Dialog, self).__init__(parent) self._info = None self._text = '' self._convertedtext = '' self._encoding = None self.mainwindow = parent self.fromVersionLabel = QLabel() self.fromVersion = QLineEdit() self.reason = QLabel() self.toVersionLabel = QLabel() self.toVersion = QLineEdit() self.lilyChooser = lilychooser.LilyChooser() self.messages = QTextBrowser() self.diff = QTextBrowser(lineWrapMode=QTextBrowser.NoWrap) self.uni_diff = QTextBrowser(lineWrapMode=QTextBrowser.NoWrap) self.copyCheck = QCheckBox(checked= QSettings().value('convert_ly/copy_messages', True, bool)) self.tabw = QTabWidget() self.tabw.addTab(self.messages, '') self.tabw.addTab(self.diff, '') self.tabw.addTab(self.uni_diff, '') self.buttons = QDialogButtonBox( QDialogButtonBox.Reset | QDialogButtonBox.Save | QDialogButtonBox.Ok | QDialogButtonBox.Cancel) self.buttons.button(QDialogButtonBox.Ok).clicked .connect(self.accept) self.buttons.rejected.connect(self.reject) self.buttons.button(QDialogButtonBox.Reset).clicked.connect(self.run) self.buttons.button(QDialogButtonBox.Save).clicked.connect(self.saveFile) layout = QVBoxLayout() self.setLayout(layout) grid = QGridLayout() grid.addWidget(self.fromVersionLabel, 0, 0) grid.addWidget(self.fromVersion, 0, 1) grid.addWidget(self.reason, 0, 2, 1, 3) grid.addWidget(self.toVersionLabel, 1, 0) grid.addWidget(self.toVersion, 1, 1) grid.addWidget(self.lilyChooser, 1, 3, 1, 2) layout.addLayout(grid) layout.addWidget(self.tabw) layout.addWidget(self.copyCheck) layout.addWidget(widgets.Separator()) layout.addWidget(self.buttons) app.translateUI(self) qutil.saveDialogSize(self, 'convert_ly/dialog/size', QSize(600, 300)) app.settingsChanged.connect(self.readSettings) self.readSettings() self.finished.connect(self.saveCopyCheckSetting) self.lilyChooser.currentIndexChanged.connect(self.slotLilyPondVersionChanged) self.slotLilyPondVersionChanged() def translateUI(self): self.fromVersionLabel.setText(_("From version:")) self.toVersionLabel.setText(_("To version:")) self.copyCheck.setText(_("Save convert-ly messages in document")) self.copyCheck.setToolTip(_( "If checked, the messages of convert-ly are appended as a " "comment to the end of the document.")) self.tabw.setTabText(0, _("&Messages")) self.tabw.setTabText(1, _("&Changes")) self.tabw.setTabText(2, _("&Diff")) self.buttons.button(QDialogButtonBox.Reset).setText(_("Run Again")) self.buttons.button(QDialogButtonBox.Save).setText(_("Save as file")) self.setCaption() def saveCopyCheckSetting(self): QSettings().setValue('convert_ly/copy_messages', self.copyCheck.isChecked()) def readSettings(self): font = textformats.formatData('editor').font self.diff.setFont(font) diffFont = QFont("Monospace") diffFont.setStyleHint(QFont.TypeWriter) self.uni_diff.setFont(diffFont) def slotLilyPondVersionChanged(self): self.setLilyPondInfo(self.lilyChooser.lilyPondInfo()) def setCaption(self): version = self._info and self._info.versionString() or _("<unknown>") title = _("Convert-ly from LilyPond {version}").format(version=version) self.setWindowTitle(app.caption(title)) def setLilyPondInfo(self, info): self._info = info self.setCaption() self.toVersion.setText(info.versionString()) self.setConvertedText() self.setDiffText() self.messages.clear() def setConvertedText(self, text=''): self._convertedtext = text self.buttons.button(QDialogButtonBox.Ok).setEnabled(bool(text)) if text: self.diff.setHtml(htmldiff.htmldiff( self._text, text, _("Current Document"), _("Converted Document"), wrapcolumn=100)) else: self.diff.clear() def setDiffText(self, text=''): if text: from_filename = "current" # TODO: maybe use real filename here to_filename = "converted" # but difflib can choke on non-ascii characters, # see https://github.com/wbsoft/frescobaldi/issues/674 difflist = list(difflib.unified_diff( self._text.split('\n'), text.split('\n'), from_filename, to_filename)) diffHLstr = self.diffHighl(difflist) self.uni_diff.setHtml(diffHLstr) else: self.uni_diff.clear() def convertedText(self): return self._convertedtext or '' def setDocument(self, doc): v = documentinfo.docinfo(doc).version_string() if v: self.fromVersion.setText(v) self.reason.setText(_("(set in document)")) else: self.reason.clear() self._text = doc.toPlainText() self._encoding = doc.encoding() or 'UTF-8' self.setConvertedText() self.setDiffText() def run(self): """Runs convert-ly (again).""" fromVersion = self.fromVersion.text() toVersion = self.toVersion.text() if not fromVersion or not toVersion: self.messages.setPlainText(_( "Both 'from' and 'to' versions need to be set.")) return info = self._info command = info.toolcommand(info.ly_tool('convert-ly')) command += ['-f', fromVersion, '-t', toVersion, '-'] # if the user wants english messages, do it also here: LANGUAGE=C env = None if os.name == "nt": # Python 2.7 subprocess on Windows chokes on unicode in env env = util.bytes_environ() else: env = dict(os.environ) if sys.platform.startswith('darwin'): try: del env['PYTHONHOME'] except KeyError: pass try: del env['PYTHONPATH'] except KeyError: pass if QSettings().value("lilypond_settings/no_translation", False, bool): if os.name == "nt": # Python 2.7 subprocess on Windows chokes on unicode in env env[b'LANGUAGE'] = b'C' else: env['LANGUAGE'] = 'C' with qutil.busyCursor(): try: proc = subprocess.Popen(command, env = env, stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE) out, err = proc.communicate(util.platform_newlines(self._text).encode(self._encoding)) except OSError as e: self.messages.setPlainText(_( "Could not start {convert_ly}:\n\n" "{message}\n").format(convert_ly = command[0], message = e)) return out = util.universal_newlines(out.decode('UTF-8')) err = util.universal_newlines(err.decode('UTF-8')) self.messages.setPlainText(err) self.setConvertedText(out) self.setDiffText(out) if not out or self._convertedtext == self._text: self.messages.append('\n' + _("The document has not been changed.")) def saveFile(self): """Save content in tab as file""" tabdata = self.getTabData(self.tabw.currentIndex()) doc = self.mainwindow.currentDocument() orgname = doc.url().toLocalFile() filename = os.path.splitext(orgname)[0] + '['+tabdata.filename+']'+'.'+tabdata.ext caption = app.caption(_("dialog title", "Save File")) filetypes = '{0} (*.txt);;{1} (*.htm);;{2} (*)'.format(_("Text Files"), _("HTML Files"), _("All Files")) filename = QFileDialog.getSaveFileName(self.mainwindow, caption, filename, filetypes)[0] if not filename: return False # cancelled with open(filename, 'wb') as f: f.write(tabdata.text.encode('utf-8')) def getTabData(self, index): """Get content of current tab from current index""" if index == 0: return FileInfo('message', 'txt', self.messages.toPlainText()) elif index == 1: return FileInfo('html-diff', 'html', self.diff.toHtml()) elif index == 2: return FileInfo('uni-diff', 'diff', self.uni_diff.toPlainText()) def diffHighl(self, difflist): """Return highlighted version of input.""" result = [] for l in difflist: if l.startswith('-'): s = '<span style="color: red; white-space: pre-wrap;">' elif l.startswith('+'): s = '<span style="color: green; white-space: pre-wrap;">' else: s = '<span style="white-space: pre-wrap;">' h = l.replace('&', '&').replace('<', '<').replace('>', '>') result.append(s + h + '</span>') return '<br>'.join(result)
class MainWindow(QMainWindow): def __init__(self, filename): super().__init__() self.setGeometry(20, 20, 1324, 1068) self.setWindowTitle('qt-Notepad') self.setStyleSheet('font-size: 14pt; font-family: Courier;') self.show() self.init_ui() centralWidget = QWidget() self.tabs = QTabWidget(centralWidget) self.setCentralWidget(self.tabs) self.tabs.setTabsClosable(True) self.tabs.setMovable(True) self.tabs.tabCloseRequested.connect(self.closeTab) if filename: f = open(filename, 'r') filedata = f.read() f.close() newfile = QTextEdit() newfile.setText(filedata) i = self.tabs.addTab(newfile, filename) self.tabs.setCurrentIndex(i) else: self.open_file() def init_ui(self): new_action = QAction('New File', self) new_action.setShortcut('Ctrl+N') new_action.setStatusTip('Create new file') new_action.triggered.connect(self.new_file) open_action = QAction('Open...', self) open_action.setShortcut('Ctrl+O') open_action.setStatusTip('Open a file') open_action.triggered.connect(self.open_file) save_action = QAction('Save File', self) save_action.setShortcut('Ctrl+S') save_action.setStatusTip('Save current file') save_action.triggered.connect(self.save_file) new_save_action = QAction('Save File As...', self) new_save_action.setShortcut('Shift+Ctrl+S') new_save_action.setStatusTip('Save current file') new_save_action.triggered.connect(self.save_file_as) close_action = QAction('Close File', self) close_action.setShortcut('Ctrl+W') close_action.setStatusTip('Close file and exit tab') close_action.triggered.connect(self.close_file) exit_action = QAction('Exit qt-Notepad', self) exit_action.setShortcut('Ctrl+Q') exit_action.setStatusTip('Close Notepad') exit_action.triggered.connect(self.close) undo_action = QAction('Undo', self) undo_action.setShortcut('Ctrl+Z') copy_action = QAction('Copy', self) copy_action.setShortcut('Ctrl+C') cut_action = QAction('Cut', self) cut_action.setShortcut('Ctrl+X') paste_action = QAction('Paste', self) paste_action.setShortcut('Ctrl+V') minimize_action = QAction('Minimize', self) minimize_action.setShortcut('Ctrl+M') view_action = QAction('Show', self) view_action.setShortcut('Ctrl+/') menubar = self.menuBar() file_menu = menubar.addMenu('&File') edit_menu = menubar.addMenu('&Edit') view_menu = menubar.addMenu('&View') window_menu = menubar.addMenu('&Window') file_menu.addAction(new_action) file_menu.addAction(open_action) file_menu.addAction(save_action) file_menu.addAction(new_save_action) file_menu.addAction(close_action) file_menu.addAction(exit_action) edit_menu.addAction(undo_action) edit_menu.addAction(copy_action) edit_menu.addAction(cut_action) edit_menu.addAction(paste_action) view_menu.addAction(view_action) window_menu.addAction(minimize_action) def closeTab(self, currentIndex): currentQWidget = self.tabs.currentWidget() currentQWidget.deleteLater() self.tabs.removeTab(currentIndex) def new_file(self): newfile = QTextEdit() i = self.tabs.addTab(newfile, 'New Document') self.tabs.setCurrentIndex(i) def save_file(self): editor = self.tabs.currentWidget() filename = self.tabs.tabText(self.tabs.currentIndex()) if filename != 'New Document': f = open(filename, 'w') filedata = editor.toPlainText() f.write(filedata) f.close() else: self.save_file_as() def save_file_as(self): filename = QFileDialog.getSaveFileName( self, 'Save File', os.getenv('HOME'))[0] print(filename) if filename != ('', ''): f = open(filename, 'w') filedata = self.text.toPlainText() f.write(filedata) f.close() def open_file(self): filename = QFileDialog.getOpenFileName( self, 'Open File', os.getenv('HOME'))[0] f = open(filename, 'r') filedata = f.read() f.close() newfile = QTextEdit() newfile.setText(filedata) i = self.tabs.addTab(newfile, filename) self.tabs.setCurrentIndex(i) def close_file(self): self.save_file() currentIndex = self.tabs.currentIndex() self.tabs.removeTab(currentIndex)